Skip to content

Commit a9b983a

Browse files
authored
Defer dynamic export of type members until first access (#192)
1 parent 3eb943b commit a9b983a

8 files changed

Lines changed: 303 additions & 153 deletions

File tree

README-DEV.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,20 @@ with no CLR.
5353
## Debugging
5454
With a debug build, the following environment variables trigger just-in-time debugging of the
5555
respective components:
56-
- `DEBUG_NODE_API_GENERATOR` - Debug the C# source-generator when it runs during the build.
57-
- `DEBUG_NODE_API_RUNTIME` - Debug the .NET runtime host when it is loaded by JavaScript. (Does
56+
- `NODE_API_DEBUG_GENERATOR=1` - Debug the C# source-generator or TS type-definitions generator
57+
when they runs during the build.
58+
- `NODE_API_DEBUG_RUNTIME=1` - Debug the .NET runtime host when it is loaded by JavaScript. (Does
5859
not apply to AOT-compiled modules.)
60+
Setting either of these variables to `1` causes the program to print a message to the console
61+
at startup and wait for a debugger to attach. Set to the string `vs` to use the VS JIT
62+
Debug dialog instead (requires Windows and a Visual Studio installation).
5963

60-
Also `TRACE_NODE_API_HOST` causes tracing information to be printed about the the process of
61-
loading the .NET host.
64+
## Tracing
65+
The following environment variables trigger verbose tracing to the console:
66+
- `NODE_API_TRACE_HOST` - Trace messages about starting the native host and managed host and
67+
dynanically exporting .NET types from the managed host to JS.
68+
- `NODE_API_TRACE_RUNTIME` - Trace all calls and callbacks across the JS/.NET boundary.
69+
Tracing works with both debug and release builds.
6270

6371
## Check/fix formatting
6472
PR builds will fail if formatting does not comply with settings in `.editorconfig`.

src/NodeApi.DotNetHost/JSMarshaller.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ public Expression<Func<JSCallbackDescriptor>> BuildConstructorOverloadDescriptor
11371137
* new Type[] { ... }, // Constructor overload parameter types
11381138
* (args) => { ... }); // Constructor overload lambda
11391139
* ... // Additional overloads
1140-
* return JSCallbackOverload.CreateDescriptor(overloads);
1140+
* return JSCallbackOverload.CreateDescriptor(typeName, overloads);
11411141
*/
11421142

11431143
ParameterExpression overloadsVariable =
@@ -1166,9 +1166,12 @@ public Expression<Func<JSCallbackDescriptor>> BuildConstructorOverloadDescriptor
11661166
}
11671167

11681168
MethodInfo createDescriptorMethod = typeof(JSCallbackOverload).GetStaticMethod(
1169-
nameof(JSCallbackOverload.CreateDescriptor));
1169+
nameof(JSCallbackOverload.CreateDescriptor),
1170+
new Type[] { typeof(string), typeof(JSCallbackOverload[]) });
11701171
statements[statements.Length - 1] = Expression.Call(
1171-
createDescriptorMethod, overloadsVariable);
1172+
createDescriptorMethod,
1173+
Expression.Constant(constructors[0].DeclaringType!.Name),
1174+
overloadsVariable);
11721175

11731176
return (Expression<Func<JSCallbackDescriptor>>)Expression.Lambda(
11741177
typeof(Func<JSCallbackDescriptor>),
@@ -1181,10 +1184,9 @@ public Expression<Func<JSCallbackDescriptor>> BuildConstructorOverloadDescriptor
11811184
}
11821185

11831186
/// <summary>
1184-
/// Builds a callback descriptor that resolves and invokes the best-matching overload from
1185-
/// a set of overloaded constructors.
1187+
/// Gets overload information for a set of constructors.
11861188
/// </summary>
1187-
public JSCallbackDescriptor BuildConstructorOverloadDescriptor(ConstructorInfo[] constructors)
1189+
public JSCallbackOverload[] GetConstructorOverloads(ConstructorInfo[] constructors)
11881190
{
11891191
JSCallbackOverload[] overloads = new JSCallbackOverload[constructors.Length];
11901192
for (int i = 0; i < constructors.Length; i++)
@@ -1203,7 +1205,7 @@ public JSCallbackDescriptor BuildConstructorOverloadDescriptor(ConstructorInfo[]
12031205
BuildFromJSConstructorExpression(constructors[i]).Compile();
12041206
overloads[i] = new JSCallbackOverload(parameterTypes, constructorDelegate);
12051207
}
1206-
return JSCallbackOverload.CreateDescriptor(overloads);
1208+
return overloads;
12071209
}
12081210

12091211
/// <summary>
@@ -1225,7 +1227,7 @@ public Expression<Func<JSCallbackDescriptor>> BuildMethodOverloadDescriptorExpre
12251227
* new Type[] { ... }, // Method overload parameter types
12261228
* (args) => { ... }); // Method overload lambda
12271229
* ... // Additional overloads
1228-
* return JSCallbackOverload.CreateDescriptor(overloads);
1230+
* return JSCallbackOverload.CreateDescriptor(methodName, overloads);
12291231
*/
12301232

12311233
ParameterExpression overloadsVariable =
@@ -1254,9 +1256,12 @@ public Expression<Func<JSCallbackDescriptor>> BuildMethodOverloadDescriptorExpre
12541256
}
12551257

12561258
MethodInfo createDescriptorMethod = typeof(JSCallbackOverload).GetStaticMethod(
1257-
nameof(JSCallbackOverload.CreateDescriptor));
1259+
nameof(JSCallbackOverload.CreateDescriptor),
1260+
new Type[] { typeof(string), typeof(JSCallbackOverload[]) });
12581261
statements[statements.Length - 1] = Expression.Call(
1259-
createDescriptorMethod, overloadsVariable);
1262+
createDescriptorMethod,
1263+
Expression.Constant(methods[0].Name),
1264+
overloadsVariable);
12601265

12611266
return (Expression<Func<JSCallbackDescriptor>>)Expression.Lambda(
12621267
typeof(Func<JSCallbackDescriptor>),
@@ -1269,10 +1274,9 @@ public Expression<Func<JSCallbackDescriptor>> BuildMethodOverloadDescriptorExpre
12691274
}
12701275

12711276
/// <summary>
1272-
/// Builds a callback descriptor that resolves and invokes the best-matching overload from
1273-
/// a set of overloaded methods.
1277+
/// Gets overload information for a set of overloaded methods.
12741278
/// </summary>
1275-
public JSCallbackDescriptor BuildMethodOverloadDescriptor(MethodInfo[] methods)
1279+
public JSCallbackOverload[] GetMethodOverloads(MethodInfo[] methods)
12761280
{
12771281
JSCallbackOverload[] overloads = new JSCallbackOverload[methods.Length];
12781282
for (int i = 0; i < methods.Length; i++)
@@ -1292,7 +1296,7 @@ public JSCallbackDescriptor BuildMethodOverloadDescriptor(MethodInfo[] methods)
12921296
BuildFromJSMethodExpression(methods[i]).Compile();
12931297
overloads[i] = new JSCallbackOverload(parameterTypes, defaultValues, methodDelegate);
12941298
}
1295-
return JSCallbackOverload.CreateDescriptor(overloads);
1299+
return overloads;
12961300
}
12971301

12981302
private Expression<JSCallback> BuildFromJSStaticMethodExpression(MethodInfo method)

src/NodeApi.DotNetHost/ManagedHost.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ JSValue removeListener(JSCallbackArgs args)
176176

177177
public static bool IsTracingEnabled { get; } =
178178
Debugger.IsAttached ||
179-
Environment.GetEnvironmentVariable("TRACE_NODE_API_HOST") == "1";
179+
Environment.GetEnvironmentVariable("NODE_API_TRACE_HOST") == "1";
180180

181181
public static void Trace(string msg)
182182
{
@@ -211,12 +211,12 @@ public static napi_value InitializeModule(napi_env env, napi_value exports)
211211
Trace($" .NET Runtime version: {Environment.Version}");
212212
#endif
213213

214-
DebugHelper.AttachDebugger("DEBUG_NODE_API_RUNTIME");
214+
DebugHelper.AttachDebugger("NODE_API_DEBUG_RUNTIME");
215215

216216
JSRuntime runtime = new NodejsRuntime();
217217

218218
if (Debugger.IsAttached ||
219-
Environment.GetEnvironmentVariable("TRACE_NODE_API_RUNTIME") != null)
219+
Environment.GetEnvironmentVariable("NODE_API_TRACE_RUNTIME") != null)
220220
{
221221
TraceSource trace = new(typeof(JSValue).Namespace!);
222222
trace.Switch.Level = SourceLevels.All;
@@ -554,9 +554,13 @@ private void LoadAssemblyTypes(Assembly assembly)
554554
continue;
555555
}
556556

557+
// Delay-loading is enabled by default, but can be disabled with this env variable.
558+
bool deferMembers = Environment.GetEnvironmentVariable("NODE_API_DELAYLOAD") != "0";
559+
557560
if (!_exportedNamespaces.TryGetValue(namespaceParts[0], out Namespace? parentNamespace))
558561
{
559-
parentNamespace = new Namespace(namespaceParts[0], _typeExporter.TryExportType);
562+
parentNamespace = new Namespace(
563+
namespaceParts[0], (type) => _typeExporter.TryExportType(type, deferMembers));
560564
_exports.GetValue()!.Value.SetProperty(namespaceParts[0], parentNamespace.Value);
561565
_exportedNamespaces.Add(namespaceParts[0], parentNamespace);
562566
}
@@ -568,7 +572,7 @@ private void LoadAssemblyTypes(Assembly assembly)
568572
{
569573
childNamespace = new Namespace(
570574
parentNamespace.Name + '.' + namespaceParts[i],
571-
_typeExporter.TryExportType);
575+
(type) => _typeExporter.TryExportType(type, deferMembers));
572576
parentNamespace.Namespaces.Add(namespaceParts[i], childNamespace);
573577
}
574578

0 commit comments

Comments
 (0)