Skip to content

Wasm marshaler registration source generator #2

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
885a771
This is a combination of 181 commits.
kg Jan 21, 2021
7a1c854
Checkpoint
kg Nov 10, 2021
13f975e
Repair merge damage
kg Nov 10, 2021
1a1d4bb
Rework create_named_function so that it can handle larger numbers of …
kg Nov 10, 2021
089e5f0
Remove unnecessary test instrumentation
kg Nov 10, 2021
78a67f2
Address PR feedback
kg Nov 10, 2021
0fdc5de
Fix closure variables being generated in the wrong place
kg Nov 10, 2021
23399f3
Fix debug code
kg Nov 10, 2021
88f8b36
eslint fixes
kg Nov 11, 2021
436442c
eslint fixes
kg Nov 11, 2021
b0cb055
Whitespace cleanup
kg Nov 17, 2021
edc54ba
Whitespace cleanup
kg Nov 17, 2021
f2dab16
Use a single memory slab for temp_malloc
kg Nov 17, 2021
97a0ccc
Checkpoint span support
kg Nov 20, 2021
8187765
Checkpoint
kg Nov 20, 2021
0f74209
Checkpoint
kg Nov 20, 2021
8098731
Fix unbuffered filters, support ReadOnlySpan
kg Nov 20, 2021
8ebd988
Fix auto signatures for primitives and add test
kg Nov 20, 2021
c2c9a5e
Use 4-chara unicode escapes since the x escapes are not officially pe…
kg Nov 24, 2021
1068a04
- MarshalerAttribute.
maraf Nov 11, 2021
6d5d929
- Integrate into System.Private.Runtime.InteropServices.JavaScript.cs…
maraf Nov 11, 2021
a34a0e5
- Move generator to a sub folder.
maraf Nov 12, 2021
66a2402
- Actual body of MarshalerInitializer.
maraf Nov 12, 2021
4be154d
- Rename generator.
maraf Nov 22, 2021
353a545
- Support for multiple attributes on single marshaler class.
maraf Nov 22, 2021
bee071a
- Tweaks.
maraf Nov 22, 2021
6799835
- Use typeof instead on string literal for marshaler types.
maraf Nov 23, 2021
79f6cce
- Remove hard-coded custom marshalers registration for linker.
maraf Nov 23, 2021
169195a
- Use MarshalerRegistrationGenerator in tests.
maraf Nov 23, 2021
3de51dc
- MarshalerAttribute generator.
maraf Nov 24, 2021
f8d4834
- Interop generator.
maraf Nov 24, 2021
5a16b92
- Fix generating marshler registration.
maraf Nov 25, 2021
c493bbb
WIP ImportFromJS and ExportToJS.
maraf Dec 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions eng/testing/tests.wasm.targets
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@
<_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" />
<_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(TargetPath)' == ''" />

<!-- the custom marshaler support requires the .csproj to include a list of custom marshalers and types
that will get included in the generated mono-config so that the runtime knows about them. Without this
any application or test relying on custom marshalers will break -->
<_WasmItemsToPass Include="@(WasmMarshaledType)" OriginalItemName__="WasmMarshaledType" />

<!-- Example of passing items to the project

<_WasmItemsToPass Include="@(BundleFiles)" OriginalItemName__="BundleFiles" ConditionToUse__="'$(Foo)' != 'true'" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F323228E-200
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{B7387D02-A1E8-4ED1-833D-65D6112F00A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.Runtime.InteropServices.JavaScript.MarshalerGenerator", "gen\MarshalerGenerator\System.Private.Runtime.InteropServices.JavaScript.MarshalerGenerator.csproj", "{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -53,6 +55,10 @@ Global
{8061897F-550F-4932-8D35-126BE3CA894C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8061897F-550F-4932-8D35-126BE3CA894C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8061897F-550F-4932-8D35-126BE3CA894C}.Release|Any CPU.Build.0 = Release|Any CPU
{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -65,6 +71,7 @@ Global
{133C6563-CA56-456A-9B63-CA5F3EA7E5CA} = {F323228E-200C-4069-98A1-E2400F3061B3}
{8061897F-550F-4932-8D35-126BE3CA894C} = {F323228E-200C-4069-98A1-E2400F3061B3}
{9B76030A-9DBD-401E-90B7-18111831B0D9} = {B7387D02-A1E8-4ED1-833D-65D6112F00A0}
{F4AEA2CF-59E2-424A-BBDE-7C9E57DC6324} = {F323228E-200C-4069-98A1-E2400F3061B3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {339DAF44-AC08-4D3B-BB34-6DA417E800E7}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// 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.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace System.Private.Runtime.InteropServices.JavaScript.MarshalerGenerator
{
[Generator]
internal class ImportFromJSGenerator : IIncrementalGenerator
{
private const string AttributeFullName = "System.Runtime.InteropServices.JavaScript.ImportFromJSAttribute";

public void Initialize(IncrementalGeneratorInitializationContext context)
{
#if DEBUG
if (!Debugger.IsAttached)
{
Debugger.Launch();
}
#endif

IncrementalValuesProvider<CompilationUnitSyntax> units = context.SyntaxProvider
.CreateSyntaxProvider(
static (s, _) => IsClassWithImportMethod(s),
static (ctx, _) => (CompilationUnitSyntax)ctx.Node
)
.Where(static m => m is not null);

IncrementalValueProvider<(Compilation, ImmutableArray<CompilationUnitSyntax>)> compilationAndUnits = context.CompilationProvider.Combine(units.Collect());
var items = context.AnalyzerConfigOptionsProvider.Combine(compilationAndUnits);
var items2 = context.AdditionalTextsProvider.Combine(items).Where(a =>
{
return a.Right.Left.GetOptions(a.Left).TryGetValue("build_metadata.AdditionalFiles.SourceItemGroup", out var sourceItemGroup) && sourceItemGroup == "CustomMarshaler";
});

context.RegisterSourceOutput(items2, static (spc, source) => Execute(source.Right.Right.Item1, source.Right.Right.Item2, spc, source.Right.Left, source.Left));
}

private static bool IsClassWithImportMethod(SyntaxNode node)
=> node is CompilationUnitSyntax c && c.DescendantNodes().OfType<MethodDeclarationSyntax>().Any(m => m.AttributeLists.Count > 0);

private static void Execute(Compilation compilation, ImmutableArray<CompilationUnitSyntax> units, SourceProductionContext context, AnalyzerConfigOptionsProvider analyzerOptions, AdditionalText at)
{
if (units.IsDefaultOrEmpty)
return;

analyzerOptions.GlobalOptions.TryGetValue("build_property.MyGenerator_EnableLogging", out var globalValue);
Console.WriteLine(globalValue);

analyzerOptions.GetOptions(at).TryGetValue("build_metadata.AdditionalFiles.SourceItemGroup", out var localValue);
Console.WriteLine(localValue);
if (localValue == "CustomMarshaler")
{
analyzerOptions.GetOptions(at).TryGetValue("build_metadata.AdditionalFiles.Type", out var dataType);
Console.WriteLine(dataType);
}

//foreach (var file in context.AdditionalFiles)
//{
// // allow the user to override the global logging on a per-file basis
// bool emitLogging = emitLoggingGlobal;
// if (context.AnalyzerConfigOptions.GetOptions(file).TryGetValue("build_metadata.AdditionalFiles.MyGenerator_EnableLogging", out var perFileLoggingSwitch))
// {
// emitLogging = perFileLoggingSwitch.Equals("true", StringComparison.OrdinalIgnoreCase);
// }

// // add the source with or without logging...
//}

foreach (var unit in units)
{
var classes = new List<ClassDeclarationSyntax>();

foreach (var member in unit.Members)
{
if (member is ClassDeclarationSyntax type)
{
var methodHeaders = FindImportMethods(compilation, type);
if (methodHeaders.Count == 0)
continue;

var methodBodies = new List<MemberDeclarationSyntax>(methodHeaders.Count);
foreach (var method in methodHeaders)
{
methodBodies.Add(
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)
),
Identifier(method.Identifier.Text)
)
.WithModifiers(
method.Modifiers
)
.WithParameterList(
method.ParameterList
)
.WithBody(
Block()
.WithCloseBraceToken(
Token(
TriviaList(
Comment("// Invoke JS")
),
SyntaxKind.CloseBraceToken,
TriviaList()
)
)
)
);
}

classes.Add(
ClassDeclaration(
type.Identifier.Text
)
.WithModifiers(
type.Modifiers
)
.WithMembers(
List(methodBodies)
)
);
}
}

if (classes.Count == 0)
continue;

var unitPartial = CompilationUnit()
.WithUsings(
unit.Usings
)
.WithMembers(
List<MemberDeclarationSyntax>(
classes
)
);

context.AddSource(Guid.NewGuid() + ".g.cs", unitPartial.NormalizeWhitespace().ToFullString());
}
}

private static List<MethodDeclarationSyntax> FindImportMethods(Compilation compilation, ClassDeclarationSyntax type)
{
var semanticModel = compilation.GetSemanticModel(type.SyntaxTree);
var result = new List<MethodDeclarationSyntax>();
foreach (var member in type.Members)
{
if (member is MethodDeclarationSyntax method)
{
foreach (AttributeListSyntax attributeListSyntax in method.AttributeLists)
{
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
{
if (semanticModel.GetSymbolInfo(attributeSyntax).Symbol is IMethodSymbol attributeConstructorSymbol)
{
string attributeFullName = attributeConstructorSymbol.ContainingType.ToDisplayString();
if (attributeFullName == AttributeFullName)
{
result.Add(method);
}
}
}
}
}
}

return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// 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.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace System.Runtime.InteropServices.JavaScript.MarshalerGenerator
{
[Generator]
internal class InteropGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//#if DEBUG
// if (!Debugger.IsAttached)
// {
// Debugger.Launch();
// }
//#endif

IncrementalValuesProvider<ClassDeclarationSyntax> typeDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
static (s, _) => IsClassDeclarationWithAnyAttribute(s),
static (ctx, _) => (ClassDeclarationSyntax)ctx.Node
);

IncrementalValueProvider<(Compilation, ImmutableArray<ClassDeclarationSyntax>)> compilationAndClasses = context.CompilationProvider.Combine(typeDeclarations.Collect());

context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
}

private static bool IsClassDeclarationWithAnyAttribute(SyntaxNode node)
=> node is ClassDeclarationSyntax m && m.Identifier.Text == Names.Interop && m.Members.OfType<ClassDeclarationSyntax>().Any(m => m.Identifier.Text == Names.InteropRuntime);

private static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> types, SourceProductionContext context)
{
if (!types.IsDefaultOrEmpty)
return;

var unit = CompilationUnit()
.WithUsings(
SingletonList(
UsingDirective(
IdentifierName("System.Runtime.CompilerServices")
)
)
)
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
ClassDeclaration(Names.Interop)
.WithModifiers(
TokenList(new[] {
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword),
Token(SyntaxKind.PartialKeyword)}
)
)
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
ClassDeclaration(Names.InteropRuntime)
.WithModifiers(
TokenList(new[] {
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword),
Token(SyntaxKind.PartialKeyword)
})
)
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.StringKeyword)
),
Identifier(Names.InteropRuntimeInvokeJS)
)
.WithAttributeLists(
SingletonList(
AttributeList(
SingletonSeparatedList(
Attribute(
IdentifierName(nameof(MethodImplAttribute))
)
.WithArgumentList(
AttributeArgumentList(
SingletonSeparatedList(
AttributeArgument(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(MethodImplOptions)),
IdentifierName(nameof(MethodImplOptions.InternalCall))
)
)
)
)
)
)
)
)
)
.WithModifiers(
TokenList(new[] {
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword),
Token(SyntaxKind.ExternKeyword)
})
)
.WithParameterList(
ParameterList(
SeparatedList<ParameterSyntax>(new SyntaxNodeOrToken[] {
Parameter(
Identifier("str")
)
.WithType(
PredefinedType(Token(SyntaxKind.StringKeyword))
),
Token(SyntaxKind.CommaToken),
Parameter(
Identifier("exceptionalResult")
)
.WithModifiers(
TokenList(
Token(SyntaxKind.OutKeyword)
)
)
.WithType(
PredefinedType(
Token(SyntaxKind.IntKeyword)
)
)
})
)
)
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken)
)
)
)
)
)
)
);

context.AddSource("Interop.Runtime.g.cs", unit.NormalizeWhitespace().ToFullString());
}
}
}
Loading