diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/Directory.Build.props b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/Directory.Build.props
new file mode 100644
index 00000000000000..f48a524d4784ee
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/CommonJSMethodGenerator.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/CommonJSMethodGenerator.cs
new file mode 100644
index 00000000000000..98a9e3a4117b65
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/CommonJSMethodGenerator.cs
@@ -0,0 +1,139 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+
+namespace JavaScript.MarshalerGenerator
+{
+ internal class CommonJSMethodGenerator
+ {
+ public StringBuilder prolog;
+ public MethodDeclarationSyntax MethodSyntax;
+ public TypeDeclarationSyntax TypeSyntax;
+ public AttributeSyntax AttributeSyntax;
+ public IMethodSymbol MethodSymbol;
+ public IMethodSymbol AttributeSymbol;
+ public AttributeData JSAttributeData;
+ public JSMarshalerSig[] ParemeterSignatures;
+ public JSMarshalerSig ReturnSignature;
+ public MarshalerSelector MarshalerSelector;
+ public string BoundFunctionName;
+ public string AssemblyName;
+
+ public ITypeSymbol ReturnType => MethodSymbol.ReturnType;
+ public TypeSyntax ReturnTypeSyntax => ReturnType.AsTypeSyntax();
+ public string MethodName => MethodSymbol.Name;
+ public bool HasCustomMarshalers => JSAttributeData.ConstructorArguments.Length > 1;
+ public bool IsVoidMethod => ReturnType.SpecialType == SpecialType.System_Void;
+
+ public void SelectMarshalers(Compilation compilation)
+ {
+ JSMarshalerMetadata[] customMarshalers = null;
+ if (HasCustomMarshalers)
+ {
+ ImmutableArray marshalerTypes = JSAttributeData.ConstructorArguments[1].Values;
+ customMarshalers = marshalerTypes.Select(mt => ExtractMarshalerMeta(compilation, mt)).ToArray();
+ }
+
+ MarshalerSelector = new MarshalerSelector(compilation);
+ ReturnSignature = MarshalerSelector.GetArgumentSignature(prolog, customMarshalers, MethodSymbol.ReturnType);
+ for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
+ {
+ IParameterSymbol arg = MethodSymbol.Parameters[i];
+ ParemeterSignatures[i] = MarshalerSelector.GetArgumentSignature(prolog, customMarshalers, arg.Type);
+ }
+ AssemblyName = compilation.AssemblyName;
+ }
+
+ protected ArgumentSyntax CreateMarshallersSyntax()
+ {
+ ArgumentSyntax marshallersArg;
+ List marshalersTypes = HasCustomMarshalers
+ ? JSAttributeData.ConstructorArguments[1].Values.Select(a => (ITypeSymbol)a.Value).ToList()
+ : new List();
+
+ if (ReturnSignature.IsAuto)
+ {
+ marshalersTypes.Add(ReturnSignature.MarshalerType);
+ }
+ marshalersTypes.AddRange(ParemeterSignatures.Where(s => s.IsAuto).Select(s => s.MarshalerType));
+
+ if (marshalersTypes.Count > 0)
+ {
+ var marshalerInstances = marshalersTypes.Distinct(SymbolEqualityComparer.Default).Cast().Select(t =>
+ {
+ return ObjectCreationExpression(t.AsTypeSyntax()).WithArgumentList(ArgumentList());
+ });
+ marshallersArg = Argument(ImplicitArrayCreationExpression(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList(marshalerInstances))));
+ }
+ else
+ {
+ marshallersArg = Argument(LiteralExpression(SyntaxKind.NullLiteralExpression));
+ }
+
+ return marshallersArg;
+ }
+
+ protected static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDeclarationSyntax typeDeclaration)
+ {
+ var mods = AddToModifiers(StripTriviaFromModifiers(typeDeclaration.Modifiers), SyntaxKind.UnsafeKeyword);
+ return TypeDeclaration(typeDeclaration.Kind(), typeDeclaration.Identifier)
+ .WithModifiers(mods);
+ }
+
+ protected static SyntaxTokenList AddToModifiers(SyntaxTokenList modifiers, SyntaxKind modifierToAdd)
+ {
+ if (modifiers.IndexOf(modifierToAdd) >= 0)
+ return modifiers;
+
+ int idx = modifiers.IndexOf(SyntaxKind.PartialKeyword);
+ return idx >= 0
+ ? modifiers.Insert(idx, Token(modifierToAdd))
+ : modifiers.Add(Token(modifierToAdd));
+ }
+
+ protected static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenList)
+ {
+ SyntaxToken[] strippedTokens = new SyntaxToken[tokenList.Count];
+ for (int i = 0; i < tokenList.Count; i++)
+ {
+ strippedTokens[i] = tokenList[i].WithoutTrivia();
+ }
+ return new SyntaxTokenList(strippedTokens);
+ }
+
+ protected JSMarshalerMetadata ExtractMarshalerMeta(Compilation compilation, TypedConstant mt)
+ {
+ try
+ {
+ INamedTypeSymbol? marshalerType = compilation.GetTypeByMetadataName(mt.Value.ToString());
+ ITypeSymbol marshaledType = marshalerType.BaseType.TypeArguments[0];
+
+ var hasAfterJs = marshalerType.GetMembers("AfterToJavaScript").Length > 0;
+
+ return new JSMarshalerMetadata
+ {
+ MarshalerType = marshalerType,
+ MarshaledType = marshaledType,
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ AfterToJsMethod = hasAfterJs ? "AfterMarshalToJs" : null,
+ };
+ }
+ catch (Exception ex)
+ {
+ prolog.AppendLine($"Failed when processing {mt.Value} \n" + ex.Message);
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/Constants.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/Constants.cs
new file mode 100644
index 00000000000000..56f17e4948af76
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/Constants.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+namespace JavaScript.MarshalerGenerator
+{
+ internal static class Constants
+ {
+ public const int JavaScriptMarshalerArgSize = 16;
+ public const string JavaScriptMarshal = "System.Runtime.InteropServices.JavaScript.JavaScriptMarshal";
+ public const string JavaScriptPublic = "System.Runtime.InteropServices.JavaScript";
+
+ public const string JavaScriptMarshalGlobal = "global::" + JavaScriptMarshal;
+ public const string JavaScriptMarshalerSignatureGlobal = "global::System.Runtime.InteropServices.JavaScript.JavaScriptMarshalerSignature";
+ public const string ModuleInitializerAttributeGlobal = "global::System.Runtime.CompilerServices.ModuleInitializerAttribute";
+ public const string DynamicDependencyAttributeGlobal = "global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute";
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportGenerator.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportGenerator.cs
new file mode 100644
index 00000000000000..6d6febcfc9317a
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportGenerator.cs
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using JavaScript.MarshalerGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace System.Runtime.InteropServices.JavaScript
+{
+ [Generator]
+ internal class JSExportGenerator : IIncrementalGenerator
+ {
+ private const string AttributeFullName = "System.Runtime.InteropServices.JavaScript.JSExportAttribute";
+ private const string Category = "JSExport";
+ private const string Prefix = "JSExport";
+#pragma warning disable RS2008 //TODO remove this
+ public static DiagnosticDescriptor RequireStaticDD = new DiagnosticDescriptor(Prefix + "002", "JSExportAttribute requires static method", "JSExportAttribute requires static method", Category, DiagnosticSeverity.Error, true);
+ public static void Debug(SourceProductionContext context, string message)
+ {
+ var dd = new DiagnosticDescriptor(Prefix + "000", message, message, Category, DiagnosticSeverity.Warning, true);
+ context.ReportDiagnostic(Diagnostic.Create(dd, Location.None));
+ }
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ IncrementalValuesProvider methodDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ static (s, _) => IsMethodDeclarationWithAnyAttribute(s),
+ static (ctx, _) => GetMethodDeclarationsWithMarshalerAttribute(ctx)
+ )
+ .Where(static m => m is not null);
+
+ IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndClasses = context.CompilationProvider.Combine(methodDeclarations.Collect());
+
+ context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
+ }
+
+ private static bool IsMethodDeclarationWithAnyAttribute(SyntaxNode node)
+ => node is MethodDeclarationSyntax m && m.AttributeLists.Count > 0;
+
+ private static JSExportMethodGenerator GetMethodDeclarationsWithMarshalerAttribute(GeneratorSyntaxContext context)
+ {
+ var methodSyntax = (MethodDeclarationSyntax)context.Node;
+
+ foreach (AttributeListSyntax attributeListSyntax in methodSyntax.AttributeLists)
+ {
+ foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
+ {
+ IMethodSymbol attributeSymbol = context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol as IMethodSymbol;
+ if (attributeSymbol != null)
+ {
+ string fullName = attributeSymbol.ContainingType.ToDisplayString();
+ if (fullName == AttributeFullName)
+ {
+ IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax);
+ var attributeData = methodSymbol.GetAttributes();
+ AttributeData JSExportData = attributeData.Where(d => d.AttributeClass.ToDisplayString() == AttributeFullName).Single();
+
+
+ var methodGenrator = new JSExportMethodGenerator(methodSyntax, attributeSyntax, methodSymbol, attributeSymbol, JSExportData);
+
+ return methodGenrator;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static void Execute(Compilation compilation, ImmutableArray methods, SourceProductionContext context)
+ {
+ if (methods.IsDefaultOrEmpty)
+ return;
+
+ var fileText = new StringBuilder();
+ foreach (JSExportMethodGenerator method in methods)
+ {
+ if (!method.MethodSymbol.IsStatic)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(RequireStaticDD, method.MethodSyntax.GetLocation()));
+ continue;
+ }
+ try
+ {
+ method.SelectMarshalers(compilation);
+
+ string code = method.GenerateWrapper();
+ // this is just for debug
+ fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
+ fileText.Append(method.prolog.ToString());
+ fileText.AppendLine("*/\n");
+ fileText.AppendLine(code);
+ }
+ catch (Exception ex)
+ {
+ // this is just for debug
+ fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
+ fileText.AppendLine(method.MethodSyntax.ToString());
+ fileText.Append(method.prolog.ToString());
+ fileText.AppendLine(ex.ToString());
+ fileText.AppendLine("*/");
+ }
+ }
+ context.AddSource("JSExport.g.cs", fileText.ToString());
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportMethodGenerator.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportMethodGenerator.cs
new file mode 100644
index 00000000000000..c1536cd6d513c8
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSExportMethodGenerator.cs
@@ -0,0 +1,179 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace JavaScript.MarshalerGenerator
+{
+ internal sealed class JSExportMethodGenerator : CommonJSMethodGenerator
+ {
+ public string WrapperName;
+ public string RegistrationName;
+ public int Hash;
+
+ public JSExportMethodGenerator(MethodDeclarationSyntax methodSyntax,
+ AttributeSyntax attributeSyntax,
+ IMethodSymbol methodSymbol,
+ IMethodSymbol attributeSymbol,
+ AttributeData jsAttrData)
+ {
+ MethodSyntax = methodSyntax;
+ MethodSymbol = methodSymbol;
+
+ AttributeSymbol = attributeSymbol;
+ AttributeSyntax = attributeSyntax;
+ JSAttributeData = jsAttrData;
+ BoundFunctionName = jsAttrData.ConstructorArguments.Length > 0
+ ? jsAttrData.ConstructorArguments[0].Value.ToString()
+ : null;
+ TypeSyntax = methodSyntax.Parent as TypeDeclarationSyntax;
+ ParemeterSignatures = new JSMarshalerSig[MethodSymbol.Parameters.Length];
+ prolog = new StringBuilder();
+
+ int hash = 17;
+ unchecked
+ {
+ foreach (var param in MethodSymbol.Parameters)
+ {
+ hash = hash * 31 + param.Type.Name.GetHashCode();
+ }
+ }
+ Hash = Math.Abs(hash);
+ WrapperName = "__Wrapper_" + MethodName + "_" + Hash;
+ RegistrationName = "__Register_" + MethodName + "_" + Hash;
+ }
+
+ public string GenerateWrapper()
+ {
+ NamespaceDeclarationSyntax namespaceSyntax = MethodSymbol.ContainingType.ContainingNamespace.AsNamespace();
+ TypeDeclarationSyntax typeSyntax = CreateTypeDeclarationWithoutTrivia(TypeSyntax);
+
+ IEnumerable wrapperStatements = WrapperSyntax();
+ IEnumerable registerStatements = RegistrationSyntax();
+
+ MemberDeclarationSyntax wrappperMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(WrapperName))
+ .WithModifiers(TokenList(new[] { Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword) }))
+ .WithParameterList(ParameterList(SingletonSeparatedList(
+ Parameter(Identifier("buffer")).WithType(PointerType(PredefinedType(Token(SyntaxKind.VoidKeyword)))))))
+ .WithBody(Block(List(wrapperStatements)));
+
+ MemberDeclarationSyntax registerMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), Identifier(RegistrationName))
+ .WithAttributeLists(List(new AttributeListSyntax[]{
+ AttributeList(SingletonSeparatedList(Attribute(IdentifierName(Constants.ModuleInitializerAttributeGlobal)))),
+ AttributeList(SingletonSeparatedList(Attribute(IdentifierName(Constants.DynamicDependencyAttributeGlobal))
+ .WithArgumentList(AttributeArgumentList(SeparatedList(new[]{
+ AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(WrapperName))),
+ AttributeArgument(TypeOfExpression(MethodSymbol.ContainingType.AsTypeSyntax()))}
+ )))))}))
+ .WithModifiers(TokenList(new[] { Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword) }))
+ .WithBody(Block(registerStatements));
+
+ CompilationUnitSyntax syntax = CompilationUnit()
+ .WithMembers(SingletonList(namespaceSyntax
+ .WithMembers(SingletonList(typeSyntax
+ .WithMembers(List(new[] { wrappperMethod, registerMethod })
+ )))));
+
+ return syntax.NormalizeWhitespace().ToFullString();
+ }
+
+ private IEnumerable RegistrationSyntax()
+ {
+ var fullyQualifiedName = $"[{AssemblyName}]{MethodSymbol.ContainingType.ToDisplayString()}:{MethodName}";
+ var signatureArgs = new List();
+ signatureArgs.Add(Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(fullyQualifiedName))));
+ signatureArgs.Add(Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(Hash))));
+ signatureArgs.Add(Argument(BoundFunctionName != null
+ ? LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(BoundFunctionName))
+ : LiteralExpression(SyntaxKind.NullLiteralExpression)));
+ signatureArgs.Add(CreateMarshallersSyntax());
+ signatureArgs.Add(Argument(TypeOfExpression(ReturnType.AsTypeSyntax())));
+ signatureArgs.AddRange(MethodSymbol.Parameters.Select(p => Argument(TypeOfExpression(p.Type.AsTypeSyntax()))));
+
+ yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshal), IdentifierName("BindCSFunction")))
+ .WithArgumentList(ArgumentList(SeparatedList(signatureArgs))));
+ }
+
+ public IEnumerable WrapperSyntax()
+ {
+ yield return LocalDeclarationStatement(VariableDeclaration(
+ IdentifierName(Identifier(TriviaList(), SyntaxKind.VarKeyword, "var", "var", TriviaList())))
+ .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("__args"))
+ .WithInitializer(EqualsValueClause(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshal), IdentifierName("CreateArguments")))
+ .WithArgumentList(ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName("buffer"))))))))));
+
+ var statements=new List();
+ var arguments=new List();
+
+ for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
+ {
+ IParameterSymbol arg = MethodSymbol.Parameters[i];
+ ExpressionSyntax invocation = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParemeterSignatures[i].MarshalerType.AsTypeSyntax(), IdentifierName(ParemeterSignatures[i].ToManagedMethod)))
+ .WithArgumentList(ArgumentList(SingletonSeparatedList(
+ Argument(ElementAccessExpression(IdentifierName("__args"))
+ .WithArgumentList(BracketedArgumentList(SingletonSeparatedList(
+ Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i + 1))))))))));
+
+ if (ParemeterSignatures[i].NeedsCast)
+ {
+ invocation = CastExpression(MethodSymbol.Parameters[i].Type.AsTypeSyntax(), invocation);
+ }
+
+ statements.Add(LocalDeclarationStatement(VariableDeclaration(
+ IdentifierName(Identifier(TriviaList(), SyntaxKind.VarKeyword, "var", "var", TriviaList())))
+ .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(arg.Name))
+ .WithInitializer(EqualsValueClause(invocation))))));
+
+ arguments.Add(Argument(IdentifierName(arg.Name)));
+ }
+ if (IsVoidMethod)
+ {
+ statements.Add(ExpressionStatement(InvocationExpression(IdentifierName(MethodName))
+ .WithArgumentList(ArgumentList(SeparatedList(arguments)))));
+ }
+ else
+ {
+ ExpressionSyntax invocation = InvocationExpression(IdentifierName(MethodName))
+ .WithArgumentList(ArgumentList(SeparatedList(arguments)));
+ if (ReturnSignature.NeedsCast)
+ {
+ invocation = CastExpression(ReturnSignature.MarshaledType.AsTypeSyntax(), invocation);
+ }
+ statements.Add(LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier(TriviaList(), SyntaxKind.VarKeyword, "var", "var", TriviaList())))
+ .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("__res"))
+ .WithInitializer(EqualsValueClause(invocation))))));
+
+ statements.Add(ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ReturnSignature.MarshalerType.AsTypeSyntax(), IdentifierName(ReturnSignature.ToJsMethod)))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName("__res")).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("__args"), IdentifierName("Result")))})))));
+ }
+
+ yield return TryStatement(SingletonList(CatchClause()
+ .WithDeclaration(CatchDeclaration(IdentifierName("Exception")).WithIdentifier(Identifier("__ex")))
+ .WithBlock(Block(SingletonList(
+ ExpressionStatement(InvocationExpression(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshal), IdentifierName("MarshalExceptionToJs")))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName("__ex")).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("__args"), IdentifierName("Exception")))})))))))))
+ .WithBlock(Block(statements));
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportGenerator.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportGenerator.cs
new file mode 100644
index 00000000000000..57c490733b44ba
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportGenerator.cs
@@ -0,0 +1,118 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using JavaScript.MarshalerGenerator;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace System.Runtime.InteropServices.JavaScript
+{
+ [Generator]
+ internal class JsImportGenerator : IIncrementalGenerator
+ {
+ private const string AttributeFullName = "System.Runtime.InteropServices.JavaScript.JSImportAttribute";
+ private const string Category = "JsImport";
+ private const string Prefix = "JsImport";
+#pragma warning disable RS2008 //TODO remove this
+ public static DiagnosticDescriptor RequirePartialDD = new DiagnosticDescriptor(Prefix + "001", "JSImportAttribute requires partial method", "JSImportAttribute requires partial method", Category, DiagnosticSeverity.Error, true);
+ public static DiagnosticDescriptor RequireStaticDD = new DiagnosticDescriptor(Prefix + "002", "JSImportAttribute requires static method", "JSImportAttribute requires static method", Category, DiagnosticSeverity.Error, true);
+ public static void Debug(SourceProductionContext context, string message)
+ {
+ var dd = new DiagnosticDescriptor(Prefix + "000", message, message, Category, DiagnosticSeverity.Warning, true);
+ context.ReportDiagnostic(Diagnostic.Create(dd, Location.None));
+ }
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ IncrementalValuesProvider methodDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ static (s, _) => IsMethodDeclarationWithAnyAttribute(s),
+ static (ctx, _) => GetMethodDeclarationsWithMarshalerAttribute(ctx)
+ )
+ .Where(static m => m is not null);
+
+ IncrementalValueProvider<(Compilation, ImmutableArray)> compilationAndClasses = context.CompilationProvider.Combine(methodDeclarations.Collect());
+
+ context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
+ }
+
+ private static bool IsMethodDeclarationWithAnyAttribute(SyntaxNode node)
+ => node is MethodDeclarationSyntax m && m.AttributeLists.Count > 0;
+
+ private static JSImportMethodGenerator GetMethodDeclarationsWithMarshalerAttribute(GeneratorSyntaxContext context)
+ {
+ var methodSyntax = (MethodDeclarationSyntax)context.Node;
+
+ foreach (AttributeListSyntax attributeListSyntax in methodSyntax.AttributeLists)
+ {
+ foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
+ {
+ IMethodSymbol attributeSymbol = context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol as IMethodSymbol;
+ if (attributeSymbol != null)
+ {
+ string fullName = attributeSymbol.ContainingType.ToDisplayString();
+ if (fullName == AttributeFullName)
+ {
+ IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax);
+ var attributeData = methodSymbol.GetAttributes();
+ AttributeData jsImportData = attributeData.Where(d => d.AttributeClass.ToDisplayString() == AttributeFullName).Single();
+
+
+ var methodGenrator = new JSImportMethodGenerator(methodSyntax, attributeSyntax, methodSymbol, attributeSymbol, jsImportData);
+
+ return methodGenrator;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static void Execute(Compilation compilation, ImmutableArray methods, SourceProductionContext context)
+ {
+ if (methods.IsDefaultOrEmpty)
+ return;
+
+ var fileText = new StringBuilder();
+ foreach (JSImportMethodGenerator method in methods)
+ {
+ if (!method.MethodSymbol.IsPartialDefinition)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(RequirePartialDD, method.MethodSyntax.GetLocation()));
+ continue;
+ }
+ if (!method.MethodSymbol.IsStatic)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(RequireStaticDD, method.MethodSyntax.GetLocation()));
+ continue;
+ }
+ try
+ {
+ method.SelectMarshalers(compilation);
+
+ string code = method.GenerateWrapper();
+ // this is just for debug
+ fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
+ fileText.Append(method.prolog.ToString());
+ fileText.AppendLine("*/\n");
+ fileText.AppendLine(code);
+ }
+ catch (Exception ex)
+ {
+ // this is just for debug
+ fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
+ fileText.AppendLine(method.MethodSyntax.ToString());
+ fileText.Append(method.prolog.ToString());
+ fileText.AppendLine(ex.ToString());
+ fileText.AppendLine("*/");
+ }
+ }
+ context.AddSource("JsImport.g.cs", fileText.ToString());
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportMethodGenerator.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportMethodGenerator.cs
new file mode 100644
index 00000000000000..91b2a881b4435b
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSImportMethodGenerator.cs
@@ -0,0 +1,229 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace JavaScript.MarshalerGenerator
+{
+ internal sealed class JSImportMethodGenerator : CommonJSMethodGenerator
+ {
+ public string BindingName;
+
+ public JSImportMethodGenerator(MethodDeclarationSyntax methodSyntax,
+ AttributeSyntax attributeSyntax,
+ IMethodSymbol methodSymbol,
+ IMethodSymbol attributeSymbol,
+ AttributeData jsAttrData)
+ {
+ MethodSyntax = methodSyntax;
+ MethodSymbol = methodSymbol;
+ AttributeSymbol = attributeSymbol;
+ AttributeSyntax = attributeSyntax;
+ JSAttributeData = jsAttrData;
+ BoundFunctionName = jsAttrData.ConstructorArguments[0].Value.ToString();
+ TypeSyntax = methodSyntax.Parent as TypeDeclarationSyntax;
+ ParemeterSignatures = new JSMarshalerSig[MethodSymbol.Parameters.Length];
+ prolog = new StringBuilder();
+
+ uint hash = 17;
+ unchecked
+ {
+ foreach (var param in MethodSymbol.Parameters)
+ {
+ hash = hash * 31 + (uint)param.Type.Name.GetHashCode();
+ }
+ }
+ BindingName = "__Binding_" + MethodName + "_" + hash;
+ }
+
+
+ public string GenerateWrapper()
+ {
+ NamespaceDeclarationSyntax namespaceSyntax = MethodSymbol.ContainingType.ContainingNamespace.AsNamespace();
+ TypeDeclarationSyntax typeSyntax = CreateTypeDeclarationWithoutTrivia(TypeSyntax);
+
+ IEnumerable parametersWithTypes = MethodSymbol.Parameters.Select(p => Parameter(Identifier(p.Name)).WithType(p.Type.AsTypeSyntax()));
+ IEnumerable statements = new[] { BindSyntax() }
+ .Union(AllocationSyntax())
+ .Union(InitSyntax())
+ .Union(ConvertSyntax())
+ .Union(CallSyntax())
+ .Union(AfterSyntax())
+ .Union(ReturnSyntax())
+ ;
+ MemberDeclarationSyntax wrappperMethod = MethodDeclaration(ReturnTypeSyntax, Identifier(MethodName))
+ .WithModifiers(MethodSyntax.Modifiers)
+ .WithParameterList(ParameterList(SeparatedList(parametersWithTypes)))
+ .WithBody(Block(List(statements)));
+ MemberDeclarationSyntax bindingField = BindingField();
+
+ CompilationUnitSyntax syntax = CompilationUnit()
+ .WithMembers(SingletonList(namespaceSyntax
+ .WithMembers(SingletonList(typeSyntax
+ .WithMembers(List(new[] { bindingField, wrappperMethod })
+ )))));
+
+ return syntax.NormalizeWhitespace().ToFullString();
+ }
+
+ private MemberDeclarationSyntax BindingField()
+ {
+ return FieldDeclaration(VariableDeclaration(IdentifierName(Constants.JavaScriptMarshalerSignatureGlobal))
+ .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier(BindingName)))))
+ .AddModifiers(Token(SyntaxKind.StaticKeyword))
+ ;
+ }
+
+ private StatementSyntax BindSyntax()
+ {
+
+
+ var bindingParameters =
+ (new ArgumentSyntax[] {
+ // name
+ Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(BoundFunctionName))),
+ // marshallers
+ CreateMarshallersSyntax(),
+ // return type
+ Argument(TypeOfExpression(ReturnType.AsTypeSyntax())),
+ })
+ // parameter types
+ .Union(MethodSymbol.Parameters.Select(p => Argument(TypeOfExpression(p.Type.AsTypeSyntax()))));
+
+ return IfStatement(BinaryExpression(SyntaxKind.EqualsExpression, IdentifierName(BindingName), LiteralExpression(SyntaxKind.NullLiteralExpression)),
+ Block(SingletonList(
+ ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
+ IdentifierName(BindingName),
+ InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("BindJSFunction")))
+ .WithArgumentList(ArgumentList(SeparatedList(bindingParameters))))))));
+ }
+
+ private IEnumerable AllocationSyntax()
+ {
+ yield return LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier("var")))
+ .WithVariables(SeparatedList(new[]{VariableDeclarator(Identifier("__excMessage"))
+ .WithInitializer(EqualsValueClause(DefaultExpression(PredefinedType(Token(SyntaxKind.StringKeyword))))) })));
+ if (!IsVoidMethod) yield return LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier("var")))
+ .WithVariables(SeparatedList(new[]{VariableDeclarator(Identifier("__resRoot"))
+ .WithInitializer(EqualsValueClause(DefaultExpression(PredefinedType(Token(SyntaxKind.ObjectKeyword))))) })));
+ yield return LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier("var")))
+ .WithVariables(SeparatedList(new[]{VariableDeclarator(Identifier("__buffer"))
+ .WithInitializer(EqualsValueClause(
+ StackAllocArrayCreationExpression(ArrayType(PredefinedType(Token(SyntaxKind.ByteKeyword)))
+ .WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList(
+ MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(BindingName), IdentifierName("TotalBufferLength"))))))))) })));
+ yield return LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier("var")))
+ .WithVariables(SeparatedList(new[]{VariableDeclarator(Identifier("__args"))
+ .WithInitializer(EqualsValueClause(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("CreateArguments")))
+ .WithArgumentList(ArgumentList(SingletonSeparatedList(
+ Argument(IdentifierName("__buffer"))))))) })));
+ for (int i = 0; i < ParemeterSignatures.Length; i++)
+ {
+ if (ParemeterSignatures[i].NeedsCast)
+ {
+ yield return LocalDeclarationStatement(VariableDeclaration(IdentifierName(Identifier("var")))
+ .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("___"+MethodSymbol.Parameters[i].Name))
+ .WithInitializer(EqualsValueClause(CastExpression(ParemeterSignatures[i].MarshaledType.AsTypeSyntax(), IdentifierName(MethodSymbol.Parameters[i].Name)))))));
+ }
+ }
+ }
+
+ private IEnumerable InitSyntax()
+ {
+ if (IsVoidMethod) yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("InitVoid")))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName("__excMessage")).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(IdentifierName("__args"))}))));
+
+ else yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("InitResult")))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName("__excMessage")).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(IdentifierName("__resRoot")).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(IdentifierName("__args")),
+ Argument(IdentifierName(BindingName))}))));
+
+ for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
+ {
+ IParameterSymbol arg = MethodSymbol.Parameters[i];
+ yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("InitArgument")))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i+1))),
+ Argument(IdentifierName(arg.Name)).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(IdentifierName("__args")),
+ Argument(IdentifierName(BindingName))}))));
+ }
+ }
+
+ private IEnumerable ConvertSyntax()
+ {
+ for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
+ {
+ IParameterSymbol arg = MethodSymbol.Parameters[i];
+ yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParemeterSignatures[i].MarshalerType.AsTypeSyntax(), IdentifierName(ParemeterSignatures[i].ToJsMethod)))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName((ParemeterSignatures[i].NeedsCast ? "___" : "")+ arg.Name)).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(ElementAccessExpression(IdentifierName("__args"))
+ .WithArgumentList(BracketedArgumentList(SingletonSeparatedList(
+ Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i+1)))))))}))));
+ }
+ }
+
+ private IEnumerable AfterSyntax()
+ {
+ for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
+ {
+ if (ParemeterSignatures[i].AfterToJsMethod != null)
+ {
+ IParameterSymbol arg = MethodSymbol.Parameters[i];
+ yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ParemeterSignatures[i].MarshalerType.AsTypeSyntax(), IdentifierName(ParemeterSignatures[i].AfterToJsMethod)))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName((ParemeterSignatures[i].NeedsCast ? "___" : "")+ arg.Name)).WithRefOrOutKeyword(Token(SyntaxKind.RefKeyword)),
+ Argument(ElementAccessExpression(IdentifierName("__args"))
+ .WithArgumentList(BracketedArgumentList(SingletonSeparatedList(
+ Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(i+1)))))))}))));
+ }
+ }
+ }
+
+ private IEnumerable CallSyntax()
+ {
+ yield return ExpressionStatement(InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName(Constants.JavaScriptMarshalGlobal), IdentifierName("InvokeBoundJSFunction")))
+ .WithArgumentList(ArgumentList(SeparatedList(new[]{
+ Argument(IdentifierName(BindingName)),
+ Argument(IdentifierName("__args"))}))));
+ }
+
+ private IEnumerable ReturnSyntax()
+ {
+ if (!IsVoidMethod)
+ {
+ ExpressionSyntax invocation = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
+ ReturnSignature.MarshalerType.AsTypeSyntax(), IdentifierName(ReturnSignature.ToManagedMethod)))
+ .WithArgumentList(ArgumentList(SingletonSeparatedList(
+ Argument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("__args"), IdentifierName("Result"))))));
+
+ if (ReturnSignature.NeedsCast)
+ {
+ invocation = CastExpression(MethodSymbol.ReturnType.AsTypeSyntax(), invocation);
+ }
+
+ yield return ReturnStatement(invocation);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerMetadata.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerMetadata.cs
new file mode 100644
index 00000000000000..59ebca802c9937
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerMetadata.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace JavaScript.MarshalerGenerator
+{
+ public class JSMarshalerMetadata
+ {
+ public ITypeSymbol MarshaledType;
+ public ITypeSymbol MarshalerType;
+ public string ToManagedMethod;
+ public string ToJsMethod;
+ public string AfterToJsMethod;
+ public bool IsAuto;
+
+ public bool IsExactMatch(ITypeSymbol other)
+ {
+ Debug.Assert(MarshaledType != null);
+ return SymbolEqualityComparer.Default.Equals(MarshaledType, other);
+ }
+
+ public bool IsAssignableFrom(Compilation compilation, ITypeSymbol argType)
+ {
+ Debug.Assert(compilation != null);
+ Debug.Assert(argType != null);
+ Debug.Assert(MarshaledType != null);
+ // TODO what about VB ?
+ return ((CSharpCompilation)compilation).ClassifyConversion(argType, MarshaledType).IsImplicit;
+ }
+
+ public JSMarshalerSig ToSignature(bool needsCast)
+ {
+ var sig = new JSMarshalerSig
+ {
+ MarshaledType = MarshaledType,
+ MarshalerType = MarshalerType,
+ ToManagedMethod = ToManagedMethod,
+ ToJsMethod = ToJsMethod,
+ AfterToJsMethod = AfterToJsMethod,
+ IsAuto = IsAuto,
+ NeedsCast = needsCast,
+ };
+
+ return sig;
+ }
+
+ public override string ToString() => $"MarshaledType:{MarshaledType} MarshalerType:{MarshalerType} ToManagedMethod:{ToManagedMethod} ToJsMethod:{ToJsMethod} IsAuto:{IsAuto}";
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerSig.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerSig.cs
new file mode 100644
index 00000000000000..5f8c9c133f8b2b
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JSMarshalerSig.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.CodeAnalysis;
+
+namespace JavaScript.MarshalerGenerator
+{
+ public class JSMarshalerSig
+ {
+ public ITypeSymbol MarshaledType;
+ public ITypeSymbol MarshalerType;
+ public string ToManagedMethod;
+ public string ToJsMethod;
+ public string AfterToJsMethod;
+ public bool IsAuto;
+ public bool NeedsCast;
+
+ public override string ToString() => $"MarshaledType:{MarshaledType} MarshalerType:{MarshalerType} ToManagedMethod:{ToManagedMethod} ToJsMethod:{ToJsMethod} IsAuto:{IsAuto}";
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JavaScript.MarshalerGenerator.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JavaScript.MarshalerGenerator.csproj
new file mode 100644
index 00000000000000..23c1d2a7bcf1e7
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/JavaScript.MarshalerGenerator.csproj
@@ -0,0 +1,32 @@
+
+
+
+ netstandard2.0
+ enable
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/MarshalerSelector.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/MarshalerSelector.cs
new file mode 100644
index 00000000000000..ec8febd986c4c4
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/MarshalerSelector.cs
@@ -0,0 +1,279 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace JavaScript.MarshalerGenerator
+{
+ internal class MarshalerSelector
+ {
+ private JSMarshalerMetadata Void;
+ private JSMarshalerMetadata Object;
+ private JSMarshalerMetadata JSObject;
+ private JSMarshalerMetadata Exception;
+ private JSMarshalerMetadata Task;
+
+ private Compilation Compilation;
+
+
+ internal JSMarshalerSig GetArgumentSignature(StringBuilder prolog, JSMarshalerMetadata[] customMarshalers, ITypeSymbol argType)
+ {
+ foreach (var marshaler in marshalers)
+ {
+ if (marshaler.IsExactMatch(argType))
+ {
+ return marshaler.ToSignature(false);
+ };
+ }
+
+ if (customMarshalers != null) foreach (var custom in customMarshalers)
+ {
+ if (custom.IsAssignableFrom(Compilation, argType))
+ {
+ return custom.ToSignature(!custom.IsExactMatch(argType));
+ }
+ }
+
+ if (JSObject.IsAssignableFrom(Compilation, argType)) return JSObject.ToSignature(!JSObject.IsExactMatch(argType));
+ if (Exception.IsAssignableFrom(Compilation, argType)) return Exception.ToSignature(!Exception.IsExactMatch(argType));
+ if (Task.IsAssignableFrom(Compilation, argType)) return Task.ToSignature(!Task.IsExactMatch(argType));
+
+ // TODO check if it has MarshalAs or StructLayout
+ // TODO test enums, ...
+ // TODO test Nullable
+ if (argType.IsValueType)
+ {
+ throw new NotSupportedException("TODO, struct is not supported: "+ argType.AsTypeSyntax());
+ }
+
+ // classes via System.Object reference
+ return Object.ToSignature(!Object.IsExactMatch(argType));
+ }
+
+ private List marshalers = new List();
+ private JSMarshalerMetadata AddMarshaler(JSMarshalerMetadata meta)
+ {
+ marshalers.Add(meta);
+ return meta;
+ }
+
+ public MarshalerSelector(Compilation compilation)
+ {
+ Compilation = compilation;
+
+ AddPrimitive(compilation);
+ AddNullable(compilation);
+
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_String),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedString",
+ ToJsMethod = "MarshalStringToJs",
+ });
+
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_DateTime),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedDateTime",
+ ToJsMethod = "MarshalDateTimeToJs",
+ });
+
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.DateTimeOffset"),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedDateTimeOffset",
+ ToJsMethod = "MarshalDateTimeOffsetToJs",
+ });
+
+ Task = AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task"),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedTask",
+ ToJsMethod = "MarshalTaskToJs",
+ AfterToJsMethod = "AfterMarshalTaskToJs",
+ });
+
+ JSObject = AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".IJSObject"),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedIJSObject",
+ ToJsMethod = "MarshalIJSObjectToJs",
+ });
+
+ Exception = AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Exception"),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedException",
+ ToJsMethod = "MarshalExceptionToJs",
+ });
+
+ Object = AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Object),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedObject",
+ ToJsMethod = "MarshalObjectToJs",
+ });
+
+ Void = AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Void),
+ MarshalerType = null,
+ ToManagedMethod = null,
+ ToJsMethod = null,
+ });
+ }
+
+ private void AddNullable(Compilation compilation) {
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Boolean)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Boolean)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Byte)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Byte)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Int16)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Int16)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Int32)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Int32)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Int64)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Int64)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Single)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Single)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_Double)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_Double)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_IntPtr)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_IntPtr)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetSpecialType(SpecialType.System_DateTime)),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetSpecialType(SpecialType.System_DateTime)),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetTypeByMetadataName("System.Nullable`1").Construct(compilation.GetTypeByMetadataName("System.DateTimeOffset")),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptPublic + ".NullableMarshaler`1").Construct(compilation.GetTypeByMetadataName("System.DateTimeOffset")),
+ ToManagedMethod = "MarshalToManaged",
+ ToJsMethod = "MarshalToJs",
+ IsAuto = true
+ });
+ }
+
+ private void AddPrimitive(Compilation compilation)
+ {
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Boolean),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedBoolean",
+ ToJsMethod = "MarshalBooleanToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Byte),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedByte",
+ ToJsMethod = "MarshalByteToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Int16),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedInt16",
+ ToJsMethod = "MarshalInt16ToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Int32),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedInt32",
+ ToJsMethod = "MarshalInt32ToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Int64),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedInt64",
+ ToJsMethod = "MarshalInt64ToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Single),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedSingle",
+ ToJsMethod = "MarshalSingleToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_Double),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedDouble",
+ ToJsMethod = "MarshalDoubleToJs",
+ });
+ AddMarshaler(new JSMarshalerMetadata
+ {
+ MarshaledType = compilation.GetSpecialType(SpecialType.System_IntPtr),
+ MarshalerType = compilation.GetTypeByMetadataName(Constants.JavaScriptMarshal),
+ ToManagedMethod = "MarshalToManagedIntPtr",
+ ToJsMethod = "MarshalIntPtrToJs",
+ });
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/TypeSymbolExtensions.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/TypeSymbolExtensions.cs
new file mode 100644
index 00000000000000..03a26facb2ad3e
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/gen/MarshalerGenerator/TypeSymbolExtensions.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace JavaScript.MarshalerGenerator
+{
+ public static class TypeSymbolExtensions
+ {
+ public static TypeSyntax AsTypeSyntax(this ITypeSymbol type)
+ {
+ string text = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ return ParseTypeName(text);
+ }
+
+ public static NamespaceDeclarationSyntax AsNamespace(this INamespaceSymbol ns)
+ {
+ string text = ns.ToDisplayString();
+ return NamespaceDeclaration(ParseName(text));
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj
index af10e0fd5fd6af..4e71d459c260d5 100644
--- a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System.Private.Runtime.InteropServices.JavaScript.csproj
@@ -4,13 +4,42 @@
enable
$(NetCoreAppCurrent)-Browser
$(NoWarn);CA1419
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -19,6 +48,10 @@
+
+
+
+
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.CustomMarshalers.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.CustomMarshalers.cs
new file mode 100644
index 00000000000000..98ffe3effa2720
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.CustomMarshalers.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+
+namespace System.Runtime.InteropServices.JavaScript.Private
+{
+ internal partial class JavaScriptMarshalImpl
+ {
+ internal static void RegisterCustomMarshalers(JavaScriptMarshalerBase[] customMarshallers)
+ {
+ if (customMarshallers == null)
+ {
+ return;
+ }
+ for (int i = 0; i < customMarshallers.Length; i++)
+ {
+ JavaScriptMarshalerBase marshaler = customMarshallers[i];
+ if (marshaler.MarshallerJSHandle == IntPtr.Zero)
+ {
+ string? code = marshaler.GetJavaScriptCode();
+ if (code == null) throw new ArgumentException("JavaScriptCode");
+
+ var type = marshaler.MarshaledType.TypeHandle.Value;
+ var exceptionMessage = _RegisterCustomMarshaller(code, type, out var jsHandle, out var isException);
+ if (isException != 0)
+ {
+ throw new JSException(exceptionMessage);
+ }
+ marshaler.MarshallerJSHandle = jsHandle;
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.Signature.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.Signature.cs
new file mode 100644
index 00000000000000..b8c4c4dcb7f896
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.Signature.cs
@@ -0,0 +1,223 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript.Private
+{
+ internal static unsafe partial class JavaScriptMarshalImpl
+ {
+ internal static JSMarshalerSig exceptionSignature = GetExceptionSignature();
+ internal static JSMarshalerSig voidSignature = GetVoidSignature();
+
+ internal static IntPtr boolType = typeof(bool).TypeHandle.Value;
+ internal static IntPtr byteType = typeof(byte).TypeHandle.Value;
+ internal static IntPtr int16Type = typeof(short).TypeHandle.Value;
+ internal static IntPtr int32Type = typeof(int).TypeHandle.Value;
+ internal static IntPtr int64Type = typeof(long).TypeHandle.Value;
+ internal static IntPtr floatType = typeof(float).TypeHandle.Value;
+ internal static IntPtr doubleType = typeof(double).TypeHandle.Value;
+ internal static IntPtr intptrType = typeof(IntPtr).TypeHandle.Value;
+ internal static IntPtr dateTimeType = typeof(DateTime).TypeHandle.Value;
+ internal static IntPtr dateTimeOffsetType = typeof(DateTimeOffset).TypeHandle.Value;
+
+ internal static IntPtr stringType = typeof(string).TypeHandle.Value;
+ internal static IntPtr ijsObjectType = typeof(IJSObject).TypeHandle.Value;
+ internal static IntPtr objectType = typeof(object).TypeHandle.Value;
+ internal static IntPtr exceptionType = typeof(Exception).TypeHandle.Value;
+ internal static IntPtr taskType = typeof(Task).TypeHandle.Value;
+ internal static IntPtr tcsType = typeof(TaskCompletionSource).TypeHandle.Value;
+ internal static IntPtr jsExceptionType = typeof(JSException).TypeHandle.Value;
+
+ private static Dictionary methodSignatures = new Dictionary(new TypeArrayEqualityComparer());
+
+ internal static JSMarshalerSig GetExceptionSignature()
+ {
+ return new JSMarshalerSig
+ {
+ TypeHandle = typeof(Exception).TypeHandle.Value,
+ BufferLength = -1,
+ BufferOffset = -1,
+ UseRoot = 0,
+ MarshallerJSHandle = IntPtr.Zero,
+ };
+ }
+
+ internal static JSMarshalerSig GetVoidSignature()
+ {
+ return new JSMarshalerSig
+ {
+ TypeHandle = IntPtr.Zero,
+ BufferLength = -1,
+ BufferOffset = -1,
+ UseRoot = 0,
+ MarshallerJSHandle = IntPtr.Zero,
+ };
+ }
+
+ internal static JSMarshalerSig GetArgumentSignature(JavaScriptMarshalerBase[] customMarshalers, Type argType, ref int extraBufferLength)
+ {
+ if (argType.IsPrimitive)
+ {
+ return new JSMarshalerSig
+ {
+ TypeHandle = argType.TypeHandle.Value,
+ BufferLength = -1,
+ BufferOffset = -1,
+ UseRoot = 0,
+ MarshallerJSHandle = IntPtr.Zero,
+ };
+ }
+ if (marshalers.TryGetValue(argType, out var m1))
+ {
+ int argBufferLength = m1.GetFixedBufferLength();
+ var sig = new JSMarshalerSig
+ {
+ TypeHandle = m1.MarshaledType.TypeHandle.Value,
+ BufferLength = argBufferLength != 0
+ ? argBufferLength
+ : -1,
+ BufferOffset = argBufferLength == 0 ? -1 : extraBufferLength,
+ UseRoot = m1.GetUseRoot() ? 1 : 0,
+ MarshallerJSHandle = m1.MarshallerJSHandle,
+ };
+ extraBufferLength += argBufferLength;
+ return sig;
+ }
+ if (customMarshalers != null) foreach (var m3 in customMarshalers)
+ {
+ if (m3.MarshaledType.IsAssignableFrom(argType))
+ {
+ int argBufferLength = m3.GetFixedBufferLength();
+ var sig = new JSMarshalerSig
+ {
+ TypeHandle = m3.MarshaledType.TypeHandle.Value,
+ BufferLength = argBufferLength != 0
+ ? argBufferLength
+ : -1,
+ BufferOffset = argBufferLength == 0 ? -1 : extraBufferLength,
+ UseRoot = m3.GetUseRoot() ? 1 : 0,
+ MarshallerJSHandle = m3.MarshallerJSHandle,
+ };
+ extraBufferLength += argBufferLength;
+ return sig;
+ }
+ }
+ foreach (var m2 in marshalersSequence)
+ {
+ if (m2.MarshaledType.IsAssignableFrom(argType))
+ {
+ int argBufferLength = m2.GetFixedBufferLength();
+ var sig = new JSMarshalerSig
+ {
+ TypeHandle = m2.MarshaledType.TypeHandle.Value,
+ BufferLength = argBufferLength != 0
+ ? argBufferLength
+ : -1,
+ BufferOffset = argBufferLength == 0 ? -1 : extraBufferLength,
+ UseRoot = m2.GetUseRoot() ? 1 : 0,
+ MarshallerJSHandle = m2.MarshallerJSHandle,
+ };
+ extraBufferLength += argBufferLength;
+ return sig;
+ }
+ }
+ // fallback System.Object
+ return new JSMarshalerSig
+ {
+ TypeHandle = objectType,
+ BufferLength = -1,
+ BufferOffset = -1,
+ MarshallerJSHandle = IntPtr.Zero,
+ UseRoot = 1,
+ };
+ }
+ // res type is first
+ internal static JavaScriptMarshalerSignature GetMethodSignature(JavaScriptMarshalerBase[] customMarshalers, params Type[] types)
+ {
+
+ lock (methodSignatures)
+ {
+ // TODO the cache doesn't differentiate customMarshalers, just the types they marshal
+ if (methodSignatures.TryGetValue(types, out var signature))
+ {
+ // copy as it could have different JSHandle
+ return new JavaScriptMarshalerSignature
+ {
+ Header = signature.Header,
+ Sigs = signature.Sigs,
+ CustomMarshalers = signature.CustomMarshalers,
+ Types = signature.Types,
+ };
+ }
+
+ int argsCount = types.Length - 1;
+ var size = sizeof(JSMarshalerSignatureHeader) + ((2 + argsCount) * sizeof(JSMarshalerSig));
+ // this is allocated outside of GC, so that it doesn't move in memory. We send it to JS side.
+ var buffer = Marshal.AllocHGlobal(size);
+ var header = (JSMarshalerSignatureHeader*)buffer;
+ var args = (JSMarshalerSig*)(buffer + sizeof(JSMarshalerSignatureHeader));
+ signature = new JavaScriptMarshalerSignature
+ {
+ Header = (JSMarshalerSignatureHeader*)buffer,
+ Sigs = (JSMarshalerSig*)(buffer + sizeof(JSMarshalerSignatureHeader)),
+ CustomMarshalers = customMarshalers,
+ Types = types,
+ };
+
+ var extraBufferLength = 0;
+ signature.ArgumentCount = argsCount;
+ signature.Exception = exceptionSignature;
+ var resType = types[0];
+ signature.Result = resType == typeof(void)
+ ? voidSignature
+ : GetArgumentSignature(customMarshalers, resType, ref extraBufferLength);
+ for (int i = 0; i < argsCount; i++)
+ {
+ args[i] = GetArgumentSignature(customMarshalers, types[i+1], ref extraBufferLength);
+ }
+ signature.ExtraBufferLength = extraBufferLength;
+ methodSignatures.Add(types, signature);
+ return signature;
+ }
+ }
+
+ internal class TypeArrayEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(Type[]? first, Type[]? second)
+ {
+ if (first == second)
+ {
+ return true;
+ }
+ if (first == null || second == null)
+ {
+ return false;
+ }
+ if (first.Length != second.Length)
+ {
+ return false;
+ }
+ for (int i = 0; i < first.Length; i++)
+ {
+ if (first[i] != second[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public int GetHashCode(Type[] obj)
+ {
+ var hc = default(HashCode);
+ foreach (var type in obj)
+ {
+ hc.Add(type);
+ }
+ return hc.ToHashCode();
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.cs
new file mode 100644
index 00000000000000..fe9dd90d4591aa
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.cs
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript.Private
+{
+ internal static unsafe partial class JavaScriptMarshalImpl
+ {
+ internal static Function log = new Function("data", @"console.log(data)");
+ private static Dictionary marshalers = new Dictionary();
+ private static List marshalersSequence = new List();
+
+ static JavaScriptMarshalImpl()
+ {
+ ExceptionMarshaler exception = new();
+ JSObjectMarshaler jsObject = new();
+ SystemObjectMarshaler sysObject = new SystemObjectMarshaler();
+ StringMarshaler str = new();
+ TaskMarshaler task = new();
+ DateTimeMarshaler datetime = new();
+ DateTimeOffsetMarshaler datetimeOffset = new();
+
+ lock (marshalers)
+ {
+ marshalers.Add(typeof(string), str);
+ marshalers.Add(typeof(DateTime), datetime);
+ marshalers.Add(typeof(DateTimeOffset), datetimeOffset);
+
+ marshalersSequence.Add(jsObject);
+ marshalers.Add(typeof(JSObject), jsObject);
+
+ marshalersSequence.Add(exception);
+ marshalers.Add(typeof(JSException), exception);
+
+ marshalersSequence.Add(task);
+ marshalers.Add(typeof(Task), task);
+
+ marshalersSequence.Add(sysObject);
+ marshalers.Add(typeof(object), sysObject);
+ }
+ }
+
+ private static void RegisterMarshaler(JavaScriptMarshalerBase marshaler)
+ {
+ lock (marshalers)
+ {
+ if (!marshaler.MarshaledType.IsSealed)
+ {
+ marshalersSequence.Insert(0, marshaler);
+ }
+ marshalers.Add(marshaler.MarshaledType, marshaler);
+ }
+ }
+
+ internal static void ThrowException(JavaScriptMarshalerArg arg)
+ {
+ var ex = JavaScriptMarshal.MarshalToManagedException(arg);
+ if (ex != null)
+ {
+ throw ex;
+ }
+ throw new JSException("unknown exception");
+ }
+ }
+}
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.icalls.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.icalls.cs
new file mode 100644
index 00000000000000..aa933f1e192dee
--- /dev/null
+++ b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Private/JavaScriptMarshalImpl.icalls.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+#nullable disable
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+namespace System.Runtime.InteropServices.JavaScript.Private
+{
+ internal static unsafe partial class JavaScriptMarshalImpl
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static unsafe extern string _BindJSFunction(string function_name, void* signature, out IntPtr bound_function_js_handle, out int is_exception);
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void _InvokeBoundJSFunction(IntPtr bound_function_js_handle, void* data);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static unsafe extern string _BindCSFunction(string fully_qualified_name, int signature_hash, string export_as_name, void* signature, out int is_exception);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static unsafe extern string _RegisterCustomMarshaller(string factory_code, IntPtr type_handle, out IntPtr js_handle_out, out int is_exception);
+
+ [JSImport("INTERNAL.mono_wasm_resolve_task")]
+ internal static partial void _ResolveTask(IntPtr gc_task_handle, object data);
+
+ [JSImport("INTERNAL.mono_wasm_reject_task")]
+ internal static partial void _RejectTask(IntPtr gc_task_handle, Exception reason);
+
+
+ internal static readonly Dictionary> _jsHandleToTaskCompletionSource = new Dictionary>();
+
+ [JSExport("INTERNAL.mono_wasm_resolve_tcs")]
+ private static void _ResolveTaskCompletionSource(IntPtr js_tcs_handle, object data)
+ {
+ TaskCompletionSource