-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Restructure JSImport/JSExport generators to share more code and utilize more Microsoft.Interop.SourceGeneration shared code #107769
base: main
Are you sure you want to change the base?
Conversation
…port and GeneratedComInterface by using local functions to shim from the JS generator signatures (which take a buffer) to the more standard "separate arguments" format used by those generators. Split out the MarshalerType/Bind logic to not run through the generators, which allows removing the IJSMarshallingGenerator concept.
...ystem.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Marshaling/BaseJSGenerator.cs
Show resolved
Hide resolved
0bf3ad1
to
3cd828e
Compare
…rator-shared-code
…ask has not resolved.
...braries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSExportGenerator.cs
Outdated
Show resolved
Hide resolved
…ImportGenerator/JSExportGenerator.cs
@@ -71,70 +71,116 @@ internal static partial void Annotated(object a1, long a2, long a3, global::Syst | |||
{ | |||
if (__signature_Annotated_1583225186 == null) | |||
{ | |||
__signature_Annotated_1583225186 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("DoesNotExist", null, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[] { global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTimeOffset), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64) }); | |||
__signature_Annotated_1583225186 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("DoesNotExist", null, [global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Discard, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Action(), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Function(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Span(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.ArraySegment(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Byte), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Array(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Object), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime, global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.DateTime), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int52), global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.BigInt64)]); |
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.
Maybe we could create separate PR, which would break this long line into multiple lines, so that it's easier to diff/review. Merge it first and into this PR.
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.
I mean, can roslyn inject new line before each argument ?
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.
With the way we're building up the syntax and manipulating it, it's difficult to do that well.
One of the eventual follow-ons from this PR is to move all of the interop generators to generate strings (at which point custom whitespace is easy), but we're not quite there yet.
…insky/runtime into js-generator-shared-code
…return arguments before we do any marshalling (instead of after).
For return values, here's the old codegen for return types (to compare against the Old Code Gen// JSImports.g.cs
// <auto-generated/>
unsafe partial class Foo
{
[global::System.Diagnostics.DebuggerNonUserCode]
public static partial global::System.Threading.Tasks.Task<int> Func()
{
if (__signature_Func_408569705 == null)
{
__signature_Func_408569705 = global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.BindJSFunction("Func", null, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[] { global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32) });
}
global::System.Span<global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument> __arguments_buffer = stackalloc global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument[2];
ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_exception = ref __arguments_buffer[0];
__arg_exception.Initialize();
ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __arg_return = ref __arguments_buffer[1];
__arg_return.Initialize();
global::System.Threading.Tasks.Task<int> __retVal;
global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding.InvokeJS(__signature_Func_408569705, __arguments_buffer);
// UnmarshalCapture - Capture the native data into marshaller instances in case conversion to managed data throws an exception.
__arg_return.ToManaged(out __retVal, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, out int __task_result) =>
{
__task_result_arg.ToManaged(out __task_result);
});
return __retVal;
}
static global::System.Runtime.InteropServices.JavaScript.JSFunctionBinding __signature_Func_408569705;
} // JSExports.g.cs
// <auto-generated/>
namespace System.Runtime.InteropServices.JavaScript
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
unsafe class __GeneratedInitializer
{
[global::System.ThreadStaticAttribute]
static bool initialized;
[global::System.Runtime.CompilerServices.ModuleInitializerAttribute, global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.__GeneratedInitializer", "Console")]
static internal void __TrimmingPreserve_()
{
}
[global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute("__Wrapper_NetFunc_408569705", "Foo", "Console")]
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("[Console]Foo:NetFunc", 408569705, new global::System.Runtime.InteropServices.JavaScript.JSMarshalerType[] { global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Task(global::System.Runtime.InteropServices.JavaScript.JSMarshalerType.Int32) });
}
}
}
unsafe partial class Foo
{
[global::System.Diagnostics.DebuggerNonUserCode]
internal static unsafe void __Wrapper_NetFunc_408569705(global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument* __arguments_buffer)
{
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];
global::System.Threading.Tasks.Task<int> __retVal = default;
try
{
__retVal = Foo.NetFunc();
// Marshal - Convert managed data to native data.
__arg_return.ToJS(__retVal, static (ref global::System.Runtime.InteropServices.JavaScript.JSMarshalerArgument __task_result_arg, int __task_result) =>
{
__task_result_arg.ToJS(__task_result);
});
}
catch (global::System.Exception ex)
{
__arg_exception.ToJS(ex);
}
}
} |
I ran the JSImportManyArgs benchmark and I saw the following results: main: average 71.2133ms So, this PR is also a perf improvement for JSImport, about 3ms, which is 4% faster. |
Split out the
MarshalerType
/Bind
logic to not run through the generators (and instead run through the generator resolver only, which allows removing theIJSMarshallingGenerator
type and many of the different generator types (as most of them end up using the same logic as the primitive generator).Update JSFunctionBinding to not use
Unsafe.AsPointer
to allow the source generator to use collection expressions (fixing one of the rude-edit errors in #107577 by removing the stackalloc) without depending on Roslyn implementation details.Also use more pattern matching instead of
when
expressions where possible inJSGeneratorResolver
to make the giant table a little more readable in my opinion.Replat the JS generators to use the same stub generators as the
LibraryImport
andGeneratedComInterface
source generators by using local functions to shim from the JS generator signatures (which take a buffer) to the more standard "separate arguments" format used by those generators.This PR also adds support in Microsoft.Interop.SourceGeneration for an exception marshaller that corresponds to a different native location than the return location (to support the separate exception return from the argument return).