未验证 提交 c746b8ba 编写于 作者: J Jeremy Koritzinsky 提交者: GitHub

Reuse more Microsoft.Interop.SourceGeneration code in JS generators (#74949)

上级 31001a3f
......@@ -69,7 +69,7 @@ IMarshallingGenerator CreateGenerator(TypePositionInfo p)
public BlockSyntax GenerateJSExportBody()
{
StatementSyntax invoke = InvokeSyntax();
JSGeneratedStatements statements = JSGeneratedStatements.Create(_marshallers, _context, invoke);
GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context);
bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty;
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToNative(_marshallers, _context, shouldInitializeVariables);
......@@ -88,7 +88,7 @@ public BlockSyntax GenerateJSExportBody()
var tryStatements = new List<StatementSyntax>();
tryStatements.AddRange(statements.Unmarshal);
tryStatements.AddRange(statements.InvokeStatements);
tryStatements.Add(invoke);
if (!statements.GuaranteedUnmarshal.IsEmpty)
{
......@@ -98,6 +98,7 @@ public BlockSyntax GenerateJSExportBody()
}
tryStatements.AddRange(statements.NotifyForSuccessfulInvoke);
tryStatements.AddRange(statements.PinnedMarshal);
tryStatements.AddRange(statements.Marshal);
List<StatementSyntax> allStatements = setupStatements;
......
......@@ -131,28 +131,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
context.RegisterConcatenatedSyntaxOutputs(generateSingleStub.Select((data, ct) => data.Item1), "JSExports.g.cs");
}
private 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);
}
private static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDeclarationSyntax typeDeclaration)
{
return TypeDeclaration(
typeDeclaration.Kind(),
typeDeclaration.Identifier)
.WithTypeParameterList(typeDeclaration.TypeParameterList)
.WithModifiers(StripTriviaFromModifiers(typeDeclaration.Modifiers));
}
private static MemberDeclarationSyntax PrintGeneratedSource(
ContainingSyntax userDeclaredMethod,
JSSignatureContext stub,
ContainingSyntaxContext containingSyntaxContext,
BlockSyntax wrapperStatements, BlockSyntax registerStatements)
{
var WrapperName = "__Wrapper_" + userDeclaredMethod.Identifier + "_" + stub.TypesHash;
......@@ -176,35 +158,7 @@ private static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDecl
.WithBody(registerStatements);
MemberDeclarationSyntax toPrint = WrapMethodInContainingScopes(stub, wrappperMethod, registerMethod);
return toPrint;
}
private static MemberDeclarationSyntax WrapMethodInContainingScopes(JSSignatureContext stub, MemberDeclarationSyntax stubMethod, MemberDeclarationSyntax sigField)
{
// Stub should have at least one containing type
Debug.Assert(stub.StubContainingTypes.Any());
// Add stub function and JSExport declaration to the first (innermost) containing
MemberDeclarationSyntax containingType = CreateTypeDeclarationWithoutTrivia(stub.StubContainingTypes.First())
.AddMembers(stubMethod, sigField);
// Add type to the remaining containing types (skipping the first which was handled above)
foreach (TypeDeclarationSyntax typeDecl in stub.StubContainingTypes.Skip(1))
{
containingType = CreateTypeDeclarationWithoutTrivia(typeDecl)
.WithMembers(SingletonList(containingType));
}
MemberDeclarationSyntax toPrint = containingType;
// Add type to the containing namespace
if (stub.StubTypeNamespace is not null)
{
toPrint = NamespaceDeclaration(IdentifierName(stub.StubTypeNamespace))
.AddMembers(toPrint);
}
MemberDeclarationSyntax toPrint = containingSyntaxContext.WrapMembersInContainingSyntaxWithUnsafeModifier(wrappperMethod, registerMethod);
return toPrint;
}
......@@ -286,7 +240,7 @@ private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGener
// Generate stub code
var stubGenerator = new JSExportCodeGenerator(
incrementalContext.Environment,
incrementalContext.SignatureContext.ElementTypeInformation,
incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation,
incrementalContext.JSExportData,
incrementalContext.SignatureContext,
(elementInfo, ex) =>
......@@ -298,7 +252,7 @@ private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGener
BlockSyntax wrapper = stubGenerator.GenerateJSExportBody();
BlockSyntax registration = stubGenerator.GenerateJSExportRegistration();
return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, wrapper, registration), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, wrapper, registration), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
}
private static bool ShouldVisitNode(SyntaxNode syntaxNode)
......
// 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 Microsoft.Interop.JavaScript
{
internal struct JSGeneratedStatements
{
public ImmutableArray<StatementSyntax> Setup { get; init; }
public ImmutableArray<StatementSyntax> Marshal { get; init; }
public ImmutableArray<FixedStatementSyntax> Pin { get; init; }
public ImmutableArray<StatementSyntax> PinnedMarshal { get; init; }
public ImmutableArray<StatementSyntax> InvokeStatements { get; init; }
public ImmutableArray<StatementSyntax> Unmarshal { get; init; }
public ImmutableArray<StatementSyntax> NotifyForSuccessfulInvoke { get; init; }
public ImmutableArray<StatementSyntax> GuaranteedUnmarshal { get; init; }
public ImmutableArray<StatementSyntax> Cleanup { get; init; }
public static JSGeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context, StatementSyntax statementToInvoke)
{
return new JSGeneratedStatements
{
Setup = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Setup }),
Marshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Marshal }),
Pin = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Pin }).Cast<FixedStatementSyntax>().ToImmutableArray(),
PinnedMarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.PinnedMarshal }),
InvokeStatements = GenerateStatementForNativeInvoke(marshallers, context with { CurrentStage = StubCodeContext.Stage.Invoke }, statementToInvoke),
Unmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.UnmarshalCapture })
.AddRange(GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Unmarshal })),
NotifyForSuccessfulInvoke = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.NotifyForSuccessfulInvoke }),
GuaranteedUnmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.GuaranteedUnmarshal }),
Cleanup = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Cleanup }),
};
}
private static ImmutableArray<StatementSyntax> GenerateStatementsForStubContext(BoundGenerators marshallers, StubCodeContext context)
{
ImmutableArray<StatementSyntax>.Builder statementsToUpdate = ImmutableArray.CreateBuilder<StatementSyntax>();
if (marshallers.NativeReturnMarshaller.TypeInfo.ManagedType != SpecialTypeInfo.Void && (context.CurrentStage is StubCodeContext.Stage.Setup or StubCodeContext.Stage.Cleanup))
{
IEnumerable<StatementSyntax> retStatements = marshallers.NativeReturnMarshaller.Generator.Generate(marshallers.NativeReturnMarshaller.TypeInfo, context);
statementsToUpdate.AddRange(retStatements);
}
if (context.CurrentStage is StubCodeContext.Stage.UnmarshalCapture or StubCodeContext.Stage.Unmarshal or StubCodeContext.Stage.GuaranteedUnmarshal)
{
// For Unmarshal and GuaranteedUnmarshal stages, use the topologically sorted
// marshaller list to generate the marshalling statements
foreach (BoundGenerator marshaller in marshallers.AllMarshallers)
{
statementsToUpdate.AddRange(marshaller.Generator.Generate(marshaller.TypeInfo, context));
}
}
else
{
// Generate code for each parameter for the current stage in declaration order.
foreach (BoundGenerator marshaller in marshallers.NativeParameterMarshallers)
{
IEnumerable<StatementSyntax> generatedStatements = marshaller.Generator.Generate(marshaller.TypeInfo, context);
statementsToUpdate.AddRange(generatedStatements);
}
}
if (statementsToUpdate.Count > 0)
{
// Comment separating each stage
SyntaxTriviaList newLeadingTrivia = GenerateStageTrivia(context.CurrentStage);
StatementSyntax firstStatementInStage = statementsToUpdate[0];
newLeadingTrivia = newLeadingTrivia.AddRange(firstStatementInStage.GetLeadingTrivia());
statementsToUpdate[0] = firstStatementInStage.WithLeadingTrivia(newLeadingTrivia);
}
return statementsToUpdate.ToImmutable();
}
private static ImmutableArray<StatementSyntax> GenerateStatementForNativeInvoke(BoundGenerators marshallers, StubCodeContext context, StatementSyntax statementToInvoke)
{
if (context.CurrentStage != StubCodeContext.Stage.Invoke)
{
throw new ArgumentException("CurrentStage must be Invoke");
}
ImmutableArray<StatementSyntax>.Builder statementsToUpdate = ImmutableArray.CreateBuilder<StatementSyntax>();
foreach (BoundGenerator marshaller in marshallers.NativeParameterMarshallers)
{
IEnumerable<StatementSyntax> generatedStatements = marshaller.Generator.Generate(marshaller.TypeInfo, context);
statementsToUpdate.AddRange(generatedStatements);
}
statementsToUpdate.Add(statementToInvoke);
return statementsToUpdate.ToImmutable();
}
private static SyntaxTriviaList GenerateStageTrivia(StubCodeContext.Stage stage)
{
string comment = stage switch
{
StubCodeContext.Stage.Setup => "Perform required setup.",
StubCodeContext.Stage.Marshal => "Convert managed data to native data.",
StubCodeContext.Stage.Pin => "Pin data in preparation for calling the P/Invoke.",
StubCodeContext.Stage.PinnedMarshal => "Convert managed data to native data that requires the managed data to be pinned.",
StubCodeContext.Stage.Invoke => "Call the P/Invoke.",
StubCodeContext.Stage.UnmarshalCapture => "Capture the native data into marshaller instances in case conversion to managed data throws an exception.",
StubCodeContext.Stage.Unmarshal => "Convert native data to managed data.",
StubCodeContext.Stage.Cleanup => "Perform required cleanup.",
StubCodeContext.Stage.NotifyForSuccessfulInvoke => "Keep alive any managed objects that need to stay alive across the call.",
StubCodeContext.Stage.GuaranteedUnmarshal => "Convert native data to managed data even in the case of an exception during the non-cleanup phases.",
_ => throw new ArgumentOutOfRangeException(nameof(stage))
};
// Comment separating each stage
return TriviaList(Comment($"// {stage} - {comment}"));
}
}
}
......@@ -77,7 +77,7 @@ IMarshallingGenerator CreateGenerator(TypePositionInfo p)
public BlockSyntax GenerateJSImportBody()
{
StatementSyntax invoke = InvokeSyntax();
JSGeneratedStatements statements = JSGeneratedStatements.Create(_marshallers, _context, invoke);
GeneratedStatements statements = GeneratedStatements.Create(_marshallers, _context);
bool shouldInitializeVariables = !statements.GuaranteedUnmarshal.IsEmpty || !statements.Cleanup.IsEmpty;
VariableDeclarations declarations = VariableDeclarations.GenerateDeclarationsForManagedToNative(_marshallers, _context, shouldInitializeVariables);
......@@ -96,8 +96,9 @@ public BlockSyntax GenerateJSImportBody()
var tryStatements = new List<StatementSyntax>();
tryStatements.AddRange(statements.Marshal);
tryStatements.AddRange(statements.PinnedMarshal);
tryStatements.AddRange(statements.InvokeStatements);
tryStatements.Add(invoke);
if (!statements.GuaranteedUnmarshal.IsEmpty)
{
tryStatements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
......
......@@ -146,25 +146,17 @@ private static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenLis
return new SyntaxTokenList(strippedTokens);
}
private static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDeclarationSyntax typeDeclaration)
{
return TypeDeclaration(
typeDeclaration.Kind(),
typeDeclaration.Identifier)
.WithTypeParameterList(typeDeclaration.TypeParameterList)
.WithModifiers(StripTriviaFromModifiers(typeDeclaration.Modifiers));
}
private static MemberDeclarationSyntax PrintGeneratedSource(
ContainingSyntax userDeclaredMethod,
JSSignatureContext stub,
ContainingSyntaxContext containingSyntaxContext,
BlockSyntax stubCode)
{
// Create stub function
MethodDeclarationSyntax stubMethod = MethodDeclaration(stub.StubReturnType, userDeclaredMethod.Identifier)
.AddAttributeLists(stub.AdditionalAttributes.ToArray())
MethodDeclarationSyntax stubMethod = MethodDeclaration(stub.SignatureContext.StubReturnType, userDeclaredMethod.Identifier)
.AddAttributeLists(stub.SignatureContext.AdditionalAttributes.ToArray())
.WithModifiers(StripTriviaFromModifiers(userDeclaredMethod.Modifiers))
.WithParameterList(ParameterList(SeparatedList(stub.StubParameters)))
.WithParameterList(ParameterList(SeparatedList(stub.SignatureContext.StubParameters)))
.WithBody(stubCode);
FieldDeclarationSyntax sigField = FieldDeclaration(VariableDeclaration(IdentifierName(Constants.JSFunctionSignatureGlobal))
......@@ -173,35 +165,7 @@ private static TypeDeclarationSyntax CreateTypeDeclarationWithoutTrivia(TypeDecl
.WithAttributeLists(SingletonList(AttributeList(SingletonSeparatedList(
Attribute(IdentifierName(Constants.ThreadStaticGlobal))))));
MemberDeclarationSyntax toPrint = WrapMethodInContainingScopes(stub, stubMethod, sigField);
return toPrint;
}
private static MemberDeclarationSyntax WrapMethodInContainingScopes(JSSignatureContext stub, MemberDeclarationSyntax stubMethod, MemberDeclarationSyntax sigField)
{
// Stub should have at least one containing type
Debug.Assert(stub.StubContainingTypes.Any());
// Add stub function and JSImport declaration to the first (innermost) containing
MemberDeclarationSyntax containingType = CreateTypeDeclarationWithoutTrivia(stub.StubContainingTypes.First())
.AddMembers(stubMethod, sigField);
// Add type to the remaining containing types (skipping the first which was handled above)
foreach (TypeDeclarationSyntax typeDecl in stub.StubContainingTypes.Skip(1))
{
containingType = CreateTypeDeclarationWithoutTrivia(typeDecl)
.WithMembers(SingletonList(containingType));
}
MemberDeclarationSyntax toPrint = containingType;
// Add type to the containing namespace
if (stub.StubTypeNamespace is not null)
{
toPrint = NamespaceDeclaration(IdentifierName(stub.StubTypeNamespace))
.AddMembers(toPrint);
}
MemberDeclarationSyntax toPrint = containingSyntaxContext.WrapMembersInContainingSyntaxWithUnsafeModifier(stubMethod, sigField);
return toPrint;
}
......@@ -290,7 +254,7 @@ private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGener
// Generate stub code
var stubGenerator = new JSImportCodeGenerator(
incrementalContext.Environment,
incrementalContext.SignatureContext.ElementTypeInformation,
incrementalContext.SignatureContext.SignatureContext.ElementTypeInformation,
incrementalContext.JSImportData,
incrementalContext.SignatureContext,
(elementInfo, ex) =>
......@@ -301,7 +265,7 @@ private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, JSGener
BlockSyntax code = stubGenerator.GenerateJSImportBody();
return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, code), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
return (PrintGeneratedSource(incrementalContext.StubMethodSyntaxTemplate, incrementalContext.SignatureContext, incrementalContext.ContainingSyntaxContext, code), incrementalContext.Diagnostics.AddRange(diagnostics.Diagnostics));
}
private static bool ShouldVisitNode(SyntaxNode syntaxNode)
......
// 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.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.Interop.JavaScript
{
internal sealed class JSSignatureContext : IEquatable<JSSignatureContext>
{
private static SymbolDisplayFormat s_typeNameFormat { get; } = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes
);
internal static readonly string GeneratorName = typeof(JSImportGenerator).Assembly.GetName().Name;
internal static readonly string GeneratorVersion = typeof(JSImportGenerator).Assembly.GetName().Version.ToString();
internal static bool MethodIsSkipLocalsInit(StubEnvironment env, IMethodSymbol method)
{
if (env.ModuleSkipLocalsInit)
{
return true;
}
if (method.GetAttributes().Any(IsSkipLocalsInitAttribute))
{
return true;
}
for (INamedTypeSymbol type = method.ContainingType; type is not null; type = type.ContainingType)
{
if (type.GetAttributes().Any(IsSkipLocalsInitAttribute))
{
return true;
}
}
// We check the module case earlier, so we don't need to do it here.
return false;
static bool IsSkipLocalsInitAttribute(AttributeData a)
=> a.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_SkipLocalsInitAttribute;
}
public static JSSignatureContext Create(
IMethodSymbol method,
StubEnvironment env,
GeneratorDiagnostics diagnostics,
CancellationToken token)
{
// Cancel early if requested
token.ThrowIfCancellationRequested();
// Determine the namespace
string? stubTypeNamespace = null;
if (!(method.ContainingNamespace is null)
&& !method.ContainingNamespace.IsGlobalNamespace)
{
stubTypeNamespace = method.ContainingNamespace.ToString();
}
string stubTypeFullName = "";
// Determine containing type(s)
ImmutableArray<TypeDeclarationSyntax>.Builder containingTypes = ImmutableArray.CreateBuilder<TypeDeclarationSyntax>();
INamedTypeSymbol currType = method.ContainingType;
while (!(currType is null))
{
// Use the declaring syntax as a basis for this type declaration.
// Since we're generating source for the method, we know that the current type
// has to be declared in source.
TypeDeclarationSyntax typeDecl = (TypeDeclarationSyntax)currType.DeclaringSyntaxReferences[0].GetSyntax(token);
// Remove current members, attributes, and base list so we don't double declare them.
typeDecl = typeDecl.WithMembers(List<MemberDeclarationSyntax>())
.WithAttributeLists(List<AttributeListSyntax>())
.WithBaseList(null);
containingTypes.Add(typeDecl);
stubTypeFullName = currType.Name + (string.IsNullOrEmpty(stubTypeFullName) ? "" : ".") + stubTypeFullName;
currType = currType.ContainingType;
}
stubTypeFullName = stubTypeNamespace == null ? stubTypeFullName : (stubTypeNamespace + "." + stubTypeFullName);
(ImmutableArray<TypePositionInfo> typeInfos, IMarshallingGeneratorFactory generatorFactory) = GenerateTypeInformation(method, diagnostics, env);
ImmutableArray<AttributeListSyntax>.Builder additionalAttrs = ImmutableArray.CreateBuilder<AttributeListSyntax>();
if (env.TargetFramework != TargetFramework.Unknown)
{
// Define additional attributes for the stub definition.
additionalAttrs.Add(
AttributeList(
SingletonSeparatedList(
Attribute(ParseName(TypeNames.System_CodeDom_Compiler_GeneratedCodeAttribute),
AttributeArgumentList(
SeparatedList(
new[]
{
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GeneratorName))),
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GeneratorVersion)))
}))))));
}
// there could be multiple method signatures with the same name, get unique signature name
uint hash = 17;
unchecked
{
foreach (var param in typeInfos)
{
hash = hash * 31 + (uint)param.ManagedType.FullTypeName.GetHashCode();
}
};
int typesHash = Math.Abs((int)hash);
var fullName = $"{method.ContainingType.ToDisplayString()}.{method.Name}";
string qualifiedName = GetFullyQualifiedMethodName(env, method);
return new JSSignatureContext()
{
StubReturnType = method.ReturnType.AsTypeSyntax(),
ElementTypeInformation = typeInfos,
StubTypeNamespace = stubTypeNamespace,
TypesHash = typesHash,
StubTypeFullName = stubTypeFullName,
StubContainingTypes = containingTypes.ToImmutable(),
AdditionalAttributes = additionalAttrs.ToImmutable(),
MethodName = fullName,
QualifiedMethodName = qualifiedName,
BindingName = "__signature_" + method.Name + "_" + typesHash,
GeneratorFactory = generatorFactory
};
}
private static string GetFullyQualifiedMethodName(StubEnvironment env, IMethodSymbol method)
{
// Mono style nested class name format.
string typeName = method.ContainingType.ToDisplayString(s_typeNameFormat).Replace(".", "/");
if (!method.ContainingType.ContainingNamespace.IsGlobalNamespace)
typeName = $"{method.ContainingType.ContainingNamespace.ToDisplayString()}.{typeName}";
return $"[{env.Compilation.AssemblyName}]{typeName}:{method.Name}";
}
private static (ImmutableArray<TypePositionInfo>, IMarshallingGeneratorFactory) GenerateTypeInformation(IMethodSymbol method, GeneratorDiagnostics diagnostics, StubEnvironment env)
{
ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation));
var jsMarshallingAttributeParser = new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(new FallbackJSMarshallingInfoProvider()));
// Determine parameter and return types
ImmutableArray<TypePositionInfo>.Builder typeInfos = ImmutableArray.CreateBuilder<TypePositionInfo>();
for (int i = 0; i < method.Parameters.Length; i++)
{
IParameterSymbol param = method.Parameters[i];
MarshallingInfo jsMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(param.Type, param.GetAttributes());
var typeInfo = TypePositionInfo.CreateForParameter(param, jsMarshallingInfo, env.Compilation) with
{
ManagedIndex = i,
NativeIndex = typeInfos.Count,
};
typeInfos.Add(typeInfo);
}
MarshallingInfo retJSMarshallingInfo = jsMarshallingAttributeParser.ParseMarshallingInfo(method.ReturnType, method.GetReturnTypeAttributes());
var retTypeInfo = new TypePositionInfo(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(method.ReturnType), retJSMarshallingInfo)
{
ManagedIndex = TypePositionInfo.ReturnIndex,
NativeIndex = TypePositionInfo.ReturnIndex,
MarshallingAttributeInfo = retJSMarshallingInfo,
};
typeInfos.Add(retTypeInfo);
var jsGeneratorFactory = new JSGeneratorFactory();
return (typeInfos.ToImmutable(), jsGeneratorFactory);
}
public ImmutableArray<TypePositionInfo> ElementTypeInformation { get; init; }
public string? StubTypeNamespace { get; init; }
public string? StubTypeFullName { get; init; }
public int TypesHash { get; init; }
public ImmutableArray<TypeDeclarationSyntax> StubContainingTypes { get; init; }
public TypeSyntax StubReturnType { get; init; }
public IEnumerable<ParameterSyntax> StubParameters
{
get
{
foreach (TypePositionInfo typeInfo in ElementTypeInformation)
{
if (typeInfo.ManagedIndex != TypePositionInfo.UnsetIndex
&& typeInfo.ManagedIndex != TypePositionInfo.ReturnIndex)
{
yield return Parameter(Identifier(typeInfo.InstanceIdentifier))
.WithType(typeInfo.ManagedType.Syntax)
.WithModifiers(TokenList(Token(typeInfo.RefKindSyntax)));
}
}
}
}
public ImmutableArray<AttributeListSyntax> AdditionalAttributes { get; init; }
public string MethodName { get; init; }
public string QualifiedMethodName { get; init; }
public string BindingName { get; init; }
public IMarshallingGeneratorFactory GeneratorFactory { get; init; }
public override bool Equals(object obj)
{
return obj is JSSignatureContext other && Equals(other);
}
public bool Equals(JSSignatureContext other)
{
// We don't check if the generator factories are equal since
// the generator factory is deterministically created based on the ElementTypeInformation and Options.
return other is not null
&& StubTypeNamespace == other.StubTypeNamespace
&& StubTypeFullName == other.StubTypeFullName
&& ElementTypeInformation.SequenceEqual(other.ElementTypeInformation)
&& StubContainingTypes.SequenceEqual(other.StubContainingTypes, (IEqualityComparer<TypeDeclarationSyntax>)SyntaxEquivalentComparer.Instance)
&& StubReturnType.IsEquivalentTo(other.StubReturnType)
&& AdditionalAttributes.SequenceEqual(other.AdditionalAttributes, (IEqualityComparer<AttributeListSyntax>)SyntaxEquivalentComparer.Instance)
;
}
public override int GetHashCode()
{
throw new UnreachableException();
}
}
}
// 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.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis;
namespace Microsoft.Interop.JavaScript
{
internal sealed record JSSignatureContext
{
private static SymbolDisplayFormat TypeAndContainingTypesStyle { get; } = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes
);
private static SymbolDisplayFormat TypeContainingTypesAndNamespacesStyle { get; } = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
internal static readonly string GeneratorName = typeof(JSImportGenerator).Assembly.GetName().Name;
internal static readonly string GeneratorVersion = typeof(JSImportGenerator).Assembly.GetName().Version.ToString();
public SignatureContext SignatureContext { get; private init; }
public static JSSignatureContext Create(
IMethodSymbol method,
StubEnvironment env,
GeneratorDiagnostics diagnostics,
CancellationToken token)
{
// Cancel early if requested
token.ThrowIfCancellationRequested();
ImmutableArray<IUseSiteAttributeParser> useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation));
var jsMarshallingAttributeParser = new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(new JSMarshalAsAttributeParser(env.Compilation)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(new FallbackJSMarshallingInfoProvider()));
SignatureContext sigContext = SignatureContext.Create(method, jsMarshallingAttributeParser, env, typeof(JSImportGenerator).Assembly);
string stubTypeFullName = method.ContainingType.ToDisplayString(TypeContainingTypesAndNamespacesStyle);
// there could be multiple method signatures with the same name, get unique signature name
uint hash = 17;
unchecked
{
foreach (var param in sigContext.ElementTypeInformation)
{
hash = hash * 31 + (uint)param.ManagedType.FullTypeName.GetHashCode();
}
};
int typesHash = Math.Abs((int)hash);
var fullName = $"{method.ContainingType.ToDisplayString()}.{method.Name}";
string qualifiedName = GetFullyQualifiedMethodName(env, method);
return new JSSignatureContext()
{
SignatureContext = sigContext,
TypesHash = typesHash,
StubTypeFullName = stubTypeFullName,
MethodName = fullName,
QualifiedMethodName = qualifiedName,
BindingName = "__signature_" + method.Name + "_" + typesHash
};
}
private static string GetFullyQualifiedMethodName(StubEnvironment env, IMethodSymbol method)
{
// Mono style nested class name format.
string typeName = method.ContainingType.ToDisplayString(TypeAndContainingTypesStyle).Replace(".", "/");
if (!method.ContainingType.ContainingNamespace.IsGlobalNamespace)
typeName = $"{method.ContainingType.ContainingNamespace.ToDisplayString()}.{typeName}";
return $"[{env.Compilation.AssemblyName}]{typeName}:{method.Name}";
}
public string? StubTypeFullName { get; init; }
public int TypesHash { get; init; }
public string MethodName { get; init; }
public string QualifiedMethodName { get; init; }
public string BindingName { get; init; }
public override int GetHashCode()
{
throw new UnreachableException();
}
}
}
......@@ -53,7 +53,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
.Select(a => a.Syntax)
.ToArray();
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.UnmarshalCapture && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source, jsty);
}
......@@ -68,7 +68,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.PinnedMarshal && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source, jsty);
}
......
......@@ -32,7 +32,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
? Argument(IdentifierName(context.GetIdentifiers(info).native))
: _inner.AsArgument(info, context);
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.UnmarshalCapture && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return ToManagedMethod(target, source);
}
......@@ -47,7 +47,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.PinnedMarshal && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return ToJSMethod(target, source);
}
......
......@@ -45,7 +45,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
? Argument(IdentifierName(context.GetIdentifiers(info).native))
: _inner.AsArgument(info, context);
if (context.CurrentStage == StubCodeContext.Stage.Unmarshal && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.UnmarshalCapture && context.Direction == CustomTypeMarshallingDirection.In && info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToManagedMethodVoid(target, source)
......@@ -64,7 +64,7 @@ public override IEnumerable<StatementSyntax> Generate(TypePositionInfo info, Stu
yield return x;
}
if (context.CurrentStage == StubCodeContext.Stage.Invoke && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
if (context.CurrentStage == StubCodeContext.Stage.PinnedMarshal && context.Direction == CustomTypeMarshallingDirection.In && !info.IsManagedReturnPosition)
{
yield return jsty.ResultTypeInfo is JSSimpleTypeInfo(KnownManagedType.Void)
? ToJSMethodVoid(target, source)
......
......@@ -88,7 +88,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
AnyDiagnosticsSink diagnostics = new();
StubEnvironment env = context.Compilation.CreateStubEnvironment();
AttributeData dllImportAttribute = method.GetAttributes().First(attr => attr.AttributeClass.ToDisplayString() == TypeNames.DllImportAttribute);
SignatureContext targetSignatureContext = SignatureContext.Create(method, CreateInteropAttributeDataFromDllImport(dllImportData), env, diagnostics, dllImportAttribute, typeof(ConvertToLibraryImportAnalyzer).Assembly);
SignatureContext targetSignatureContext = SignatureContext.Create(method, DefaultMarshallingInfoParser.Create(env, diagnostics, method, CreateInteropAttributeDataFromDllImport(dllImportData), dllImportAttribute), env, typeof(ConvertToLibraryImportAnalyzer).Assembly);
var generatorFactoryKey = LibraryImportGeneratorHelpers.CreateGeneratorFactory(env, new LibraryImportGeneratorOptions(context.Options.AnalyzerConfigOptionsProvider.GlobalOptions));
......
// 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 Microsoft.CodeAnalysis;
namespace Microsoft.Interop
{
internal static class DefaultMarshallingInfoParser
{
public static MarshallingInfoParser Create(StubEnvironment env, IGeneratorDiagnostics diagnostics, IMethodSymbol method, InteropAttributeData interopAttributeData, AttributeData unparsedAttributeData)
{
// Compute the current default string encoding value.
CharEncoding defaultEncoding = CharEncoding.Undefined;
if (interopAttributeData.IsUserDefined.HasFlag(InteropAttributeMember.StringMarshalling))
{
defaultEncoding = interopAttributeData.StringMarshalling switch
{
StringMarshalling.Utf16 => CharEncoding.Utf16,
StringMarshalling.Utf8 => CharEncoding.Utf8,
StringMarshalling.Custom => CharEncoding.Custom,
_ => CharEncoding.Undefined, // [Compat] Do not assume a specific value
};
}
else if (interopAttributeData.IsUserDefined.HasFlag(InteropAttributeMember.StringMarshallingCustomType))
{
defaultEncoding = CharEncoding.Custom;
}
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding, interopAttributeData.StringMarshallingCustomType);
var useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics));
return new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics),
new NativeMarshallingAttributeParser(env.Compilation, diagnostics)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(
new SafeHandleMarshallingInfoProvider(env.Compilation, method.ContainingType),
new ArrayMarshallingInfoProvider(env.Compilation),
new CharMarshallingInfoProvider(defaultInfo),
new StringMarshallingInfoProvider(env.Compilation, diagnostics, unparsedAttributeData, defaultInfo),
new BooleanMarshallingInfoProvider(),
new BlittableTypeMarshallingInfoProvider(env.Compilation)));
}
}
}
......@@ -312,7 +312,7 @@ private static SyntaxTokenList StripTriviaFromModifiers(SyntaxTokenList tokenLis
}
// Create the stub.
var signatureContext = SignatureContext.Create(symbol, libraryImportData, environment, generatorDiagnostics, generatedDllImportAttr, typeof(LibraryImportGenerator).Assembly);
var signatureContext = SignatureContext.Create(symbol, DefaultMarshallingInfoParser.Create(environment, generatorDiagnostics, symbol, libraryImportData, generatedDllImportAttr), environment, typeof(LibraryImportGenerator).Assembly);
var containingTypeContext = new ContainingSyntaxContext(originalSyntax);
......
......@@ -102,5 +102,31 @@ public MemberDeclarationSyntax WrapMemberInContainingSyntaxWithUnsafeModifier(Me
}
return wrappedMember;
}
public MemberDeclarationSyntax WrapMembersInContainingSyntaxWithUnsafeModifier(params MemberDeclarationSyntax[] members)
{
bool addedUnsafe = false;
MemberDeclarationSyntax? wrappedMember = null;
foreach (var containingType in ContainingSyntax)
{
TypeDeclarationSyntax type = TypeDeclaration(containingType.TypeKind, containingType.Identifier)
.WithModifiers(containingType.Modifiers)
.AddMembers(wrappedMember is not null ? new[] { wrappedMember } : members);
if (!addedUnsafe)
{
type = type.WithModifiers(type.Modifiers.AddToModifiers(SyntaxKind.UnsafeKeyword));
}
if (containingType.TypeParameters is not null)
{
type = type.AddTypeParameterListParameters(containingType.TypeParameters.Parameters.ToArray());
}
wrappedMember = type;
}
if (ContainingNamespace is not null)
{
wrappedMember = NamespaceDeclaration(ParseName(ContainingNamespace)).AddMembers(wrappedMember);
}
return wrappedMember;
}
}
}
......@@ -25,7 +25,7 @@ public struct GeneratedStatements
public ImmutableArray<StatementSyntax> GuaranteedUnmarshal { get; init; }
public ImmutableArray<StatementSyntax> Cleanup { get; init; }
public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context, ExpressionSyntax expressionToInvoke)
public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context)
{
return new GeneratedStatements
{
......@@ -33,7 +33,7 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo
Marshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Marshal }),
Pin = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Pin }).Cast<FixedStatementSyntax>().ToImmutableArray(),
PinnedMarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.PinnedMarshal }),
InvokeStatement = GenerateStatementForNativeInvoke(marshallers, context with { CurrentStage = StubCodeContext.Stage.Invoke }, expressionToInvoke),
InvokeStatement = EmptyStatement(),
Unmarshal = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.UnmarshalCapture })
.AddRange(GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Unmarshal })),
NotifyForSuccessfulInvoke = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.NotifyForSuccessfulInvoke }),
......@@ -41,6 +41,13 @@ public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeCo
Cleanup = GenerateStatementsForStubContext(marshallers, context with { CurrentStage = StubCodeContext.Stage.Cleanup }),
};
}
public static GeneratedStatements Create(BoundGenerators marshallers, StubCodeContext context, ExpressionSyntax expressionToInvoke)
{
return Create(marshallers, context) with
{
InvokeStatement = GenerateStatementForNativeInvoke(marshallers, context with { CurrentStage = StubCodeContext.Stage.Invoke }, expressionToInvoke)
};
}
private static ImmutableArray<StatementSyntax> GenerateStatementsForStubContext(BoundGenerators marshallers, StubCodeContext context)
{
......
......@@ -53,13 +53,11 @@ public IEnumerable<ParameterSyntax> StubParameters
public static SignatureContext Create(
IMethodSymbol method,
InteropAttributeData interopAttributeData,
MarshallingInfoParser marshallingInfoParser,
StubEnvironment env,
IGeneratorDiagnostics diagnostics,
AttributeData signatureWideMarshallingAttributeData,
Assembly generatorInfoAssembly)
{
ImmutableArray<TypePositionInfo> typeInfos = GenerateTypeInformation(method, interopAttributeData, diagnostics, env, signatureWideMarshallingAttributeData);
ImmutableArray<TypePositionInfo> typeInfos = GenerateTypeInformation(method, marshallingInfoParser, env);
ImmutableArray<AttributeListSyntax>.Builder additionalAttrs = ImmutableArray.CreateBuilder<AttributeListSyntax>();
......@@ -102,49 +100,9 @@ public IEnumerable<ParameterSyntax> StubParameters
private static ImmutableArray<TypePositionInfo> GenerateTypeInformation(
IMethodSymbol method,
InteropAttributeData interopAttributeData,
IGeneratorDiagnostics diagnostics,
StubEnvironment env,
AttributeData signatureWideMarshallingAttributeData)
MarshallingInfoParser marshallingInfoParser,
StubEnvironment env)
{
// Compute the current default string encoding value.
CharEncoding defaultEncoding = CharEncoding.Undefined;
if (interopAttributeData.IsUserDefined.HasFlag(InteropAttributeMember.StringMarshalling))
{
defaultEncoding = interopAttributeData.StringMarshalling switch
{
StringMarshalling.Utf16 => CharEncoding.Utf16,
StringMarshalling.Utf8 => CharEncoding.Utf8,
StringMarshalling.Custom => CharEncoding.Custom,
_ => CharEncoding.Undefined, // [Compat] Do not assume a specific value
};
}
else if (interopAttributeData.IsUserDefined.HasFlag(InteropAttributeMember.StringMarshallingCustomType))
{
defaultEncoding = CharEncoding.Custom;
}
var defaultInfo = new DefaultMarshallingInfo(defaultEncoding, interopAttributeData.StringMarshallingCustomType);
var useSiteAttributeParsers = ImmutableArray.Create<IUseSiteAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics));
var marshallingInfoParser = new MarshallingInfoParser(
diagnostics,
new MethodSignatureElementInfoProvider(env.Compilation, diagnostics, method, useSiteAttributeParsers),
useSiteAttributeParsers,
ImmutableArray.Create<IMarshallingInfoAttributeParser>(
new MarshalAsAttributeParser(env.Compilation, diagnostics, defaultInfo),
new MarshalUsingAttributeParser(env.Compilation, diagnostics),
new NativeMarshallingAttributeParser(env.Compilation, diagnostics)),
ImmutableArray.Create<ITypeBasedMarshallingInfoProvider>(
new SafeHandleMarshallingInfoProvider(env.Compilation, method.ContainingType),
new ArrayMarshallingInfoProvider(env.Compilation),
new CharMarshallingInfoProvider(defaultInfo),
new StringMarshallingInfoProvider(env.Compilation, diagnostics, signatureWideMarshallingAttributeData, defaultInfo),
new BooleanMarshallingInfoProvider(),
new BlittableTypeMarshallingInfoProvider(env.Compilation)));
// Determine parameter and return types
ImmutableArray<TypePositionInfo>.Builder typeInfos = ImmutableArray.CreateBuilder<TypePositionInfo>();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册