Skip to content

Commit aacf17f

Browse files
committed
rebase
1 parent 6019cda commit aacf17f

File tree

64 files changed

+6540
-19
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+6540
-19
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project>
2+
<PropertyGroup>
3+
<IsGeneratorProject>true</IsGeneratorProject>
4+
</PropertyGroup>
5+
<Import Project="..\Directory.Build.props" />
6+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.Linq;
8+
using System.Text;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
13+
14+
15+
namespace JavaScript.MarshalerGenerator
16+
{
17+
internal class CommonJSMethodGenerator
18+
{
19+
public StringBuilder prolog;
20+
public MethodDeclarationSyntax MethodSyntax;
21+
public TypeDeclarationSyntax TypeSyntax;
22+
public AttributeSyntax AttributeSyntax;
23+
public IMethodSymbol MethodSymbol;
24+
public IMethodSymbol AttributeSymbol;
25+
public AttributeData JSAttributeData;
26+
public JSMarshalerSig[] ParemeterSignatures;
27+
public JSMarshalerSig ReturnSignature;
28+
public MarshalerSelector MarshalerSelector;
29+
public string BoundFunctionName;
30+
public string AssemblyName;
31+
32+
public ITypeSymbol ReturnType => MethodSymbol.ReturnType;
33+
public TypeSyntax ReturnTypeSyntax => ReturnType.AsTypeSyntax();
34+
public string MethodName => MethodSymbol.Name;
35+
public bool HasCustomMarshalers => JSAttributeData.ConstructorArguments.Length > 1;
36+
public bool IsVoidMethod => ReturnType.SpecialType == SpecialType.System_Void;
37+
38+
public void SelectMarshalers(Compilation compilation)
39+
{
40+
JSMarshalerMetadata[] customMarshalers = null;
41+
if (HasCustomMarshalers)
42+
{
43+
ImmutableArray<TypedConstant> marshalerTypes = JSAttributeData.ConstructorArguments[1].Values;
44+
customMarshalers = marshalerTypes.Select(mt => ExtractMarshalerMeta(compilation, mt)).ToArray();
45+
}
46+
47+
MarshalerSelector = new MarshalerSelector(compilation);
48+
ReturnSignature = MarshalerSelector.GetArgumentSignature(prolog, customMarshalers, MethodSymbol.ReturnType);
49+
for (int i = 0; i < MethodSymbol.Parameters.Length; i++)
50+
{
51+
IParameterSymbol arg = MethodSymbol.Parameters[i];
52+
ParemeterSignatures[i] = MarshalerSelector.GetArgumentSignature(prolog, customMarshalers, arg.Type);
53+
}
54+
AssemblyName = compilation.AssemblyName;
55+
}
56+
57+
protected ArgumentSyntax CreateMarshallersSyntax()
58+
{
59+
ArgumentSyntax marshallersArg;
60+
List<ITypeSymbol> marshalersTypes = HasCustomMarshalers
61+
? JSAttributeData.ConstructorArguments[1].Values.Select(a => (ITypeSymbol)a.Value).ToList()
62+
: new List<ITypeSymbol?>();
63+
64+
if (ReturnSignature.IsAuto)
65+
{
66+
marshalersTypes.Add(ReturnSignature.MarshalerType);
67+
}
68+
marshalersTypes.AddRange(ParemeterSignatures.Where(s => s.IsAuto).Select(s => s.MarshalerType));
69+
70+
if (marshalersTypes.Count > 0)
71+
{
72+
var marshalerInstances = marshalersTypes.Distinct(SymbolEqualityComparer.Default).Cast<ITypeSymbol>().Select(t =>
73+
{
74+
return ObjectCreationExpression(t.AsTypeSyntax()).WithArgumentList(ArgumentList());
75+
});
76+
marshallersArg = Argument(ImplicitArrayCreationExpression(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList<ExpressionSyntax>(marshalerInstances))));
77+
}
78+
else
79+
{
80+
marshallersArg = Argument(LiteralExpression(SyntaxKind.NullLiteralExpression));
81+
}
82+
83+
return marshallersArg;
84+
}
85+
86+
protected static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDeclarationSyntax typeDeclaration)
87+
{
88+
var mods = AddToModifiers(StripTriviaFromModifiers(typeDeclaration.Modifiers), SyntaxKind.UnsafeKeyword);
89+
return TypeDeclaration(typeDeclaration.Kind(), typeDeclaration.Identifier)
90+
.WithModifiers(mods);
91+
}
92+
93+
protected static SyntaxTokenList AddToModifiers(SyntaxTokenList modifiers, SyntaxKind modifierToAdd)
94+
{
95+
if (modifiers.IndexOf(modifierToAdd) >= 0)
96+
return modifiers;
97+
98+
int idx = modifiers.IndexOf(SyntaxKind.PartialKeyword);
99+
return idx >= 0
100+
? modifiers.Insert(idx, Token(modifierToAdd))
101+
: modifiers.Add(Token(modifierToAdd));
102+
}
103+
104+
protected static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenList)
105+
{
106+
SyntaxToken[] strippedTokens = new SyntaxToken[tokenList.Count];
107+
for (int i = 0; i < tokenList.Count; i++)
108+
{
109+
strippedTokens[i] = tokenList[i].WithoutTrivia();
110+
}
111+
return new SyntaxTokenList(strippedTokens);
112+
}
113+
114+
protected JSMarshalerMetadata ExtractMarshalerMeta(Compilation compilation, TypedConstant mt)
115+
{
116+
try
117+
{
118+
INamedTypeSymbol? marshalerType = compilation.GetTypeByMetadataName(mt.Value.ToString());
119+
ITypeSymbol marshaledType = marshalerType.BaseType.TypeArguments[0];
120+
121+
var hasAfterJs = marshalerType.GetMembers("AfterToJavaScript").Length > 0;
122+
123+
return new JSMarshalerMetadata
124+
{
125+
MarshalerType = marshalerType,
126+
MarshaledType = marshaledType,
127+
ToManagedMethod = "MarshalToManaged",
128+
ToJsMethod = "MarshalToJs",
129+
AfterToJsMethod = hasAfterJs ? "AfterMarshalToJs" : null,
130+
};
131+
}
132+
catch (Exception ex)
133+
{
134+
prolog.AppendLine($"Failed when processing {mt.Value} \n" + ex.Message);
135+
return null;
136+
}
137+
}
138+
}
139+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
5+
namespace JavaScript.MarshalerGenerator
6+
{
7+
internal static class Constants
8+
{
9+
public const int JavaScriptMarshalerArgSize = 16;
10+
public const string JavaScriptMarshal = "System.Runtime.InteropServices.JavaScript.JavaScriptMarshal";
11+
public const string JavaScriptPublic = "System.Runtime.InteropServices.JavaScript";
12+
13+
public const string JavaScriptMarshalGlobal = "global::" + JavaScriptMarshal;
14+
public const string JavaScriptMarshalerSignatureGlobal = "global::System.Runtime.InteropServices.JavaScript.JavaScriptMarshalerSignature";
15+
public const string ModuleInitializerAttributeGlobal = "global::System.Runtime.CompilerServices.ModuleInitializerAttribute";
16+
public const string DynamicDependencyAttributeGlobal = "global::System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute";
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using System.Linq;
6+
using System.Text;
7+
using JavaScript.MarshalerGenerator;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
namespace System.Runtime.InteropServices.JavaScript
13+
{
14+
[Generator]
15+
internal class JSExportGenerator : IIncrementalGenerator
16+
{
17+
private const string AttributeFullName = "System.Runtime.InteropServices.JavaScript.JSExportAttribute";
18+
private const string Category = "JSExport";
19+
private const string Prefix = "JSExport";
20+
#pragma warning disable RS2008 //TODO remove this
21+
public static DiagnosticDescriptor RequireStaticDD = new DiagnosticDescriptor(Prefix + "002", "JSExportAttribute requires static method", "JSExportAttribute requires static method", Category, DiagnosticSeverity.Error, true);
22+
public static void Debug(SourceProductionContext context, string message)
23+
{
24+
var dd = new DiagnosticDescriptor(Prefix + "000", message, message, Category, DiagnosticSeverity.Warning, true);
25+
context.ReportDiagnostic(Diagnostic.Create(dd, Location.None));
26+
}
27+
28+
public void Initialize(IncrementalGeneratorInitializationContext context)
29+
{
30+
IncrementalValuesProvider<JSExportMethodGenerator> methodDeclarations = context.SyntaxProvider
31+
.CreateSyntaxProvider(
32+
static (s, _) => IsMethodDeclarationWithAnyAttribute(s),
33+
static (ctx, _) => GetMethodDeclarationsWithMarshalerAttribute(ctx)
34+
)
35+
.Where(static m => m is not null);
36+
37+
IncrementalValueProvider<(Compilation, ImmutableArray<JSExportMethodGenerator>)> compilationAndClasses = context.CompilationProvider.Combine(methodDeclarations.Collect());
38+
39+
context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
40+
}
41+
42+
private static bool IsMethodDeclarationWithAnyAttribute(SyntaxNode node)
43+
=> node is MethodDeclarationSyntax m && m.AttributeLists.Count > 0;
44+
45+
private static JSExportMethodGenerator GetMethodDeclarationsWithMarshalerAttribute(GeneratorSyntaxContext context)
46+
{
47+
var methodSyntax = (MethodDeclarationSyntax)context.Node;
48+
49+
foreach (AttributeListSyntax attributeListSyntax in methodSyntax.AttributeLists)
50+
{
51+
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
52+
{
53+
IMethodSymbol attributeSymbol = context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol as IMethodSymbol;
54+
if (attributeSymbol != null)
55+
{
56+
string fullName = attributeSymbol.ContainingType.ToDisplayString();
57+
if (fullName == AttributeFullName)
58+
{
59+
IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax);
60+
var attributeData = methodSymbol.GetAttributes();
61+
AttributeData JSExportData = attributeData.Where(d => d.AttributeClass.ToDisplayString() == AttributeFullName).Single();
62+
63+
64+
var methodGenrator = new JSExportMethodGenerator(methodSyntax, attributeSyntax, methodSymbol, attributeSymbol, JSExportData);
65+
66+
return methodGenrator;
67+
}
68+
}
69+
}
70+
}
71+
72+
return null;
73+
}
74+
75+
private static void Execute(Compilation compilation, ImmutableArray<JSExportMethodGenerator> methods, SourceProductionContext context)
76+
{
77+
if (methods.IsDefaultOrEmpty)
78+
return;
79+
80+
var fileText = new StringBuilder();
81+
foreach (JSExportMethodGenerator method in methods)
82+
{
83+
if (!method.MethodSymbol.IsStatic)
84+
{
85+
context.ReportDiagnostic(Diagnostic.Create(RequireStaticDD, method.MethodSyntax.GetLocation()));
86+
continue;
87+
}
88+
try
89+
{
90+
method.SelectMarshalers(compilation);
91+
92+
string code = method.GenerateWrapper();
93+
// this is just for debug
94+
fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
95+
fileText.Append(method.prolog.ToString());
96+
fileText.AppendLine("*/\n");
97+
fileText.AppendLine(code);
98+
}
99+
catch (Exception ex)
100+
{
101+
// this is just for debug
102+
fileText.AppendLine("/* " + method.MethodName + " " + DateTime.Now.ToString("o"));
103+
fileText.AppendLine(method.MethodSyntax.ToString());
104+
fileText.Append(method.prolog.ToString());
105+
fileText.AppendLine(ex.ToString());
106+
fileText.AppendLine("*/");
107+
}
108+
}
109+
context.AddSource("JSExport.g.cs", fileText.ToString());
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)