-
Notifications
You must be signed in to change notification settings - Fork 5.1k
[wasm] Lazy init of [JSExport] bindings #77293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Tagging subscribers to 'arch-wasm': @lewing Issue DetailsThe original code was generating the initializer into the same class as the exported method. Next step could be to remove
|
...braries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs
Outdated
Show resolved
Hide resolved
...braries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs
Outdated
Show resolved
Hide resolved
The results look very good:
These are close to times before regression:
|
This comment was marked as resolved.
This comment was marked as resolved.
Generated code looks like this // <auto-generated/>
namespace System.Runtime.InteropServices.JavaScript
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
unsafe class __GeneratedInitializer
{
static bool initialized;
[global::System.Runtime.CompilerServices.ModuleInitializerAttribute]
static internal void __Net7SelfInit_()
{
if (Environment.Version.Major == 7)
{
__Register_();
}
}
[global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute(
"__Wrapper_ConsoleWriteLine_695648116", "System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper",
"System.Runtime.InteropServices.JavaScript.Tests")]
static void __Register_()
{
if (initialized || global::System.Runtime.InteropServices.RuntimeInformation.OSArchitecture != global::System.Runtime.InteropServices.Architecture.Wasm)
return;
initialized = true;
global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindManagedFunction(
"[System.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper:ConsoleWriteLine",
695648116, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[]{
global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard,
global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.String});
}
}
}
namespace System.Runtime.InteropServices.JavaScript.Tests
{
public unsafe partial class JavaScriptTestHelper
{
internal static unsafe void __Wrapper_ConsoleWriteLine_695648116(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* __arguments_buffer)
{
string message;
ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_exception = ref __arguments_buffer[0];
ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_return = ref __arguments_buffer[1];
// Setup - Perform required setup.
ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __message_native__js_arg = ref __arguments_buffer[2];
// Unmarshal - Convert native data to managed data.
__message_native__js_arg.ToManaged(out message);
try
{
System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper.ConsoleWriteLine(message);
}
catch (global::System.Exception ex)
{
__arg_exception.ToJS(ex);
}
}
}
} |
@jkoritzinsky when net8 SDK is targeting Net7, does it use the analyzers from the net7 targeting pack or from net8 targeting pack ? |
global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindManagedFunction(
"[System.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper:ConsoleWriteLine",
695648116, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[]{
global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard,
global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.String}); I assume the magical number is some kind of hash which we use to make sure to generate unique method name for the given method "wrapper". |
The magical number is there just to differentiate multiple methods with same name but different signature. Could we pass delegate to It also has to work in AOT scenarios. |
This is currently blocked on Net7/Net8 of |
To make this friendly to memory snapshot, we need to allow the JS side to enforce the binding refresh. Maybe |
I tested forward and backward compatibility with Net7. I will leave fixing muti-threading for the next PR. |
/azp run runtime-wasm |
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
The original code was generating the initializer into the same class as the exported method.
It triggers static constructors on classes with exported members.
That could be unexpected side-effect for the developers with performance or behavioral consequences.
__Register_
System.Runtime.InteropServices.JavaScript.__GeneratedInitializer
.__Register_
method is now called when JS side requests bindings viagetAssemblyExports
API__Net7SelfInit_
method with[ModuleInitializer]
.__Register_
from trimming.__Register_
on Net7 runtime because it doesn't call__Register_
from JS.This considers following combinations
__GeneratedInitializer
class in the assembly and call module cctor instead.__Register_
on when on Net7__GeneratedInitializer
class in the assembly and module cctor will be cheap empty operationFixes #75598