Skip to content

Commit

Permalink
New libNode API (#405)
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz authored Feb 11, 2025
1 parent 956d1b1 commit bda132b
Show file tree
Hide file tree
Showing 37 changed files with 2,518 additions and 437 deletions.
4 changes: 4 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
<Target Name="WriteVersionProps"
BeforeTargets="Pack"
>
<PropertyGroup>
<LibNodePackageVersion Condition="'%(PackageVersion.Identity)' == 'Microsoft.JavaScript.LibNode'">%(PackageVersion.Version)</LibNodePackageVersion>
</PropertyGroup>
<WriteLinesToFile
File="$(PackageOutputPath)version.props"
Overwrite="true"
WriteOnlyWhenDifferent="true"
Lines="
&lt;Project&gt;;
%20%20&lt;PropertyGroup&gt;;
%20%20%20%20&lt;LibNodePackageVersion&gt;$(LibNodePackageVersion)&lt;/LibNodePackageVersion&gt;;
%20%20%20%20&lt;NodeApiDotNetPackageVersion&gt;$(NuGetPackageVersion)&lt;/NodeApiDotNetPackageVersion&gt;;
%20%20&lt;/PropertyGroup&gt;;
&lt;/Project&gt;"
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" /><!-- 4.1.0 is compatible with .NET Standard -->
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.JavaScript.LibNode" Version="20.1800.202" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.6.133" />
Expand All @@ -16,7 +17,6 @@
<PackageVersion Include="System.Reflection.MetadataLoadContext" Version="6.0.0" />
<PackageVersion Include="xunit" Version="2.4.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.4.13" />
<PackageVersion Include="XmlDocMarkdown.Core" Version="2.9.0" />
</ItemGroup>
</Project>
29 changes: 18 additions & 11 deletions bench/Benchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public static void Main(string[] args)
GetCurrentPlatformRuntimeIdentifier(),
"libnode" + GetSharedLibraryExtension());

private napi_env _env;
private NodeEmbeddingRuntime? _runtime;
private NodeEmbeddingNodeApiScope? _nodeApiScope;
private JSValue _jsString;
private JSFunction _jsFunction;
private JSFunction _jsFunctionWithArgs;
Expand All @@ -64,6 +65,9 @@ public static void Main(string[] args)
private JSFunction _jsFunctionCallMethod;
private JSFunction _jsFunctionCallMethodWithArgs;
private JSReference _reference = null!;
private static readonly string[] s_settings = new[] { "node", "--expose-gc" };
private static string s_mainScript { get; } =
"globalThis.require = require('module').createRequire(process.execPath);\n";

/// <summary>
/// Simple class that is exported to JS and used in some benchmarks.
Expand All @@ -84,16 +88,19 @@ public static void Method() { }
/// </summary>
protected void Setup()
{
NodejsPlatform platform = new(LibnodePath/*, args: new[] { "node", "--expose-gc" }*/);

// This setup avoids using NodejsEnvironment so benchmarks can run on the same thread.
// NodejsEnvironment creates a separate thread that would slow down the micro-benchmarks.
platform.Runtime.CreateEnvironment(
platform, Console.WriteLine, null, NodejsEnvironment.NodeApiVersion, out _env)
.ThrowIfFailed();

// The new scope instance saves itself as the thread-local JSValueScope.Current.
JSValueScope scope = new(JSValueScopeType.Root, _env, platform.Runtime);
NodeEmbeddingPlatform platform = new(
LibnodePath,
new NodeEmbeddingPlatformSettings { Args = s_settings });

// This setup avoids using NodejsEmbeddingThreadRuntime so benchmarks can run on
// the same thread. NodejsEmbeddingThreadRuntime creates a separate thread that would slow
// down the micro-benchmarks.
_runtime = NodeEmbeddingRuntime.Create(platform,
new NodeEmbeddingRuntimeSettings { MainScript = s_mainScript });

// The nodeApiScope creates JSValueScope instance that saves itself as
// the thread-local JSValueScope.Current.
_nodeApiScope = new(_runtime);

// Create some JS values that will be used by the benchmarks.

Expand Down
11 changes: 8 additions & 3 deletions examples/jsdom/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ public static void Main()
{
string appDir = Path.GetDirectoryName(typeof(Program).Assembly.Location)!;
string libnodePath = Path.Combine(appDir, "libnode.dll");
using NodejsPlatform nodejsPlatform = new(libnodePath);
using NodejsEnvironment nodejs = nodejsPlatform.CreateEnvironment(appDir);
using NodeEmbeddingPlatform nodejsPlatform = new(libnodePath, null);
using NodeEmbeddingThreadRuntime nodejs = nodejsPlatform.CreateThreadRuntime(appDir,
new NodeEmbeddingRuntimeSettings
{
MainScript =
"globalThis.require = require('module').createRequire(process.execPath);\n"
});
if (Debugger.IsAttached)
{
int pid = Process.GetCurrentProcess().Id;
Expand All @@ -25,7 +30,7 @@ public static void Main()
Console.WriteLine(content);
}

private static string GetContent(NodejsEnvironment nodejs, string html)
private static string GetContent(NodeEmbeddingThreadRuntime nodejs, string html)
{
JSValue jsdomClass = nodejs.Import(module: "jsdom", property: "JSDOM");
JSValue dom = jsdomClass.CallAsConstructor(html);
Expand Down
5 changes: 1 addition & 4 deletions examples/jsdom/jsdom.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@
<ItemGroup>
<None Include="README.md" />
<Compile Include="*.cs" />
<Content Include="..\..\bin\win-x64\libnode.dll">
<Visible>false</Visible>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.JavaScript.LibNode" Version="$(LibNodePackageVersion)" />
<PackageReference Include="Microsoft.JavaScript.NodeApi" Version="$(NodeApiDotnetPackageVersion)" />
</ItemGroup>
</Project>
11 changes: 7 additions & 4 deletions examples/winui-fluid/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ public App()

string appDir = Path.GetDirectoryName(typeof(App).Assembly.Location)!;
string libnodePath = Path.Combine(appDir, "libnode.dll");
NodejsPlatform nodejsPlatform = new(libnodePath);

Nodejs = nodejsPlatform.CreateEnvironment(appDir);
NodeEmbeddingPlatform nodejsPlatform = new(libnodePath, null);
Nodejs = nodejsPlatform.CreateThreadRuntime(appDir, new NodeEmbeddingRuntimeSettings
{
MainScript =
"globalThis.require = require('module').createRequire(process.execPath);\n"
});
if (Debugger.IsAttached)
{
int pid = Process.GetCurrentProcess().Id;
Expand Down Expand Up @@ -60,5 +63,5 @@ private void OnMainWindowClosed(object sender, WindowEventArgs args)

public static new App Current => (App)Application.Current;

public NodejsEnvironment Nodejs { get; }
public NodeEmbeddingThreadRuntime Nodejs { get; }
}
2 changes: 1 addition & 1 deletion examples/winui-fluid/CollabEditBox.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public sealed partial class CollabEditBox : UserControl
private const string FluidServiceUri = "http://localhost:7070/";

private readonly SynchronizationContext uiSyncContext;
private readonly NodejsEnvironment nodejs;
private readonly NodeEmbeddingThreadRuntime nodejs;
private readonly JSMarshaller marshaller;

private ITinyliciousClient fluidClient = null!;
Expand Down
5 changes: 1 addition & 4 deletions examples/winui-fluid/winui-fluid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,10 @@

<ItemGroup>
<EmbeddedResource Include="README.md" />
<Content Include="..\..\bin\win-x64\libnode.dll">
<Visible>false</Visible>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.JavaScript.LibNode" Version="$(LibNodePackageVersion)" />
<PackageReference Include="Microsoft.JavaScript.NodeApi" Version="$(NodeApiDotnetPackageVersion)" />
<PackageReference Include="Microsoft.JavaScript.NodeApi.DotNetHost" Version="$(NodeApiDotnetPackageVersion)" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.*" />
Expand Down
2 changes: 1 addition & 1 deletion src/NodeApi.DotNetHost/JSRuntimeContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static T Import<T>(
/// <exception cref="ArgumentNullException">Both <paramref cref="module" /> and
/// <paramref cref="property" /> are null.</exception>
public static T Import<T>(
this NodejsEnvironment nodejs,
this NodeEmbeddingThreadRuntime nodejs,
string? module,
string? property,
bool esModule,
Expand Down
17 changes: 17 additions & 0 deletions src/NodeApi/Interop/EmptyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ public CallerArgumentExpressionAttribute(string parameterName)

public string ParameterName { get; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property
| AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class RequiredMemberAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute (string featureName)
{
FeatureName = featureName;
}

public string FeatureName { get; }
}
}

namespace System.Diagnostics
Expand Down
16 changes: 16 additions & 0 deletions src/NodeApi/NodeApiStatusExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.JavaScript.NodeApi.Runtime;
using static Microsoft.JavaScript.NodeApi.Runtime.JSRuntime;
using static Microsoft.JavaScript.NodeApi.Runtime.NodejsRuntime;

namespace Microsoft.JavaScript.NodeApi;

Expand Down Expand Up @@ -59,5 +61,19 @@ public static T ThrowIfFailed<T>(this napi_status status,
status.ThrowIfFailed(memberName, sourceFilePath, sourceLineNumber);
return value;
}

[StackTraceHidden]
public static void ThrowIfFailed([DoesNotReturnIf(true)] this NodeEmbeddingStatus status,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
if (status == NodeEmbeddingStatus.OK)
return;
throw new JSException($"""
Error in {memberName} at {sourceFilePath}:{sourceLineNumber}
{NodeEmbedding.JSRuntime.EmbeddingGetLastErrorMessage()}
""");
}
}

16 changes: 2 additions & 14 deletions src/NodeApi/Runtime/JSRuntime.Types.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.JavaScript.NodeApi.Runtime;

using System;
using System.Runtime.InteropServices;

namespace Microsoft.JavaScript.NodeApi.Runtime;

// Type definitions from Node.JS js_native_api.h and js_native_api_types.h
public unsafe partial class JSRuntime
{
Expand All @@ -31,7 +31,6 @@ public record struct napi_handle_scope(nint Handle);
public record struct napi_escapable_handle_scope(nint Handle);
public record struct napi_callback_info(nint Handle);
public record struct napi_deferred(nint Handle);
public record struct napi_platform(nint Handle);

//===========================================================================
// Enum types
Expand Down Expand Up @@ -141,17 +140,6 @@ public napi_finalize(napi_finalize.Delegate callback)
: this(Marshal.GetFunctionPointerForDelegate(callback)) { }
}

public struct napi_error_message_handler
{
public nint Handle;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Delegate(byte* message);

public napi_error_message_handler(napi_error_message_handler.Delegate handler)
=> Handle = Marshal.GetFunctionPointerForDelegate(handler);
}

public struct napi_property_descriptor
{
// One of utf8name or name should be NULL.
Expand Down
Loading

0 comments on commit bda132b

Please sign in to comment.