Exemplo n.º 1
0
            static IdentifierNameSyntax BuildFinalSubLoad(ref List <MemberDeclarationSyntax> methods, int key, string entryPoint, bool emitAssert, bool preloadVTable, bool vNext)
            {
                var name = NameGenerator.Name($"Load_Final_{key}_{entryPoint}");
                var body = new List <StatementSyntax>();

                if (emitAssert)
                {
                    body.Add(ExpressionStatement(
                                 InvocationExpression(
                                     MemberAccessExpression(
                                         SyntaxKind.SimpleMemberAccessExpression,
                                         MemberAccessExpression(
                                             SyntaxKind.SimpleMemberAccessExpression,
                                             MemberAccessExpression(
                                                 SyntaxKind.SimpleMemberAccessExpression,
                                                 IdentifierName("System"),
                                                 IdentifierName("Diagnostics")),
                                             IdentifierName("Debug")),
                                         IdentifierName("Assert")))
                                 .WithArgumentList(
                                     ArgumentList(
                                         SingletonSeparatedList(
                                             Argument(
                                                 BinaryExpression(
                                                     SyntaxKind.EqualsExpression,
                                                     IdentifierName(keyName),
                                                     Num(key))))))));
                }

                var localName = $"_{entryPoint}";

                if (!preloadVTable)
                {
                    body.Add
                    (
                        IfStatement
                        (
                            BinaryExpression
                            (
                                SyntaxKind.NotEqualsExpression, IdentifierName(localName),
                                DefaultExpression(IdentifierName("System.IntPtr"))
                            ), ReturnStatement(IdentifierName(localName))
                        )
                    );

                    body.Add
                    (
                        ExpressionStatement
                        (
                            AssignmentExpression
                            (
                                SyntaxKind.SimpleAssignmentExpression, IdentifierName(localName),
                                InvocationExpression
                                (
                                    MemberAccessExpression
                                    (
                                        SyntaxKind.SimpleMemberAccessExpression, IdentifierName("_ctx"),
                                        IdentifierName("GetProcAddress")
                                    ), ArgumentList
                                    (
                                        SeparatedList
                                        (
                                            new[]
                    {
                        Argument(IdentifierName("entryPoint")),
                        Argument(IdentifierName("slot"))
                    }
                                        )
                                    )
                                )
                            )
                        )
                    );
                }

                body.Add(ReturnStatement(IdentifierName(localName)));

                methods.Add
                (
                    MethodDeclaration
                        (QualifiedName(IdentifierName("System"), IdentifierName("IntPtr")), Identifier(name))
                    .WithParameterList
                    (
                        ParameterList
                        (
                            SeparatedList
                            (
                                new[]
                {
                    Parameter
                        (Identifier(keyName))
                    .WithType(PredefinedType(Token(SyntaxKind.IntKeyword))),
                    Parameter
                        (Identifier("entryPoint"))
                    .WithType(PredefinedType(Token(SyntaxKind.StringKeyword)))
                }
                            )
                        )
                    )
                    .WithAttributeLists
                    (
                        SingletonList
                        (
                            AttributeList
                            (
                                SingletonSeparatedList
                                (
                                    Attribute
                                    (
                                        QualifiedName
                                        (
                                            QualifiedName
                                            (
                                                QualifiedName
                                                    (IdentifierName("System"), IdentifierName("Runtime")),
                                                IdentifierName("CompilerServices")
                                            ), IdentifierName("MethodImpl")
                                        )
                                    )

                                    #region [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining | System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)]

                                    .WithArgumentList
                                    (
                                        AttributeArgumentList
                                        (
                                            SingletonSeparatedList
                                            (
                                                AttributeArgument
                                                (
                                                    BinaryExpression
                                                    (
                                                        SyntaxKind.BitwiseOrExpression,
                                                        MemberAccessExpression
                                                        (
                                                            SyntaxKind.SimpleMemberAccessExpression,
                                                            MemberAccessExpression
                                                            (
                                                                SyntaxKind.SimpleMemberAccessExpression,
                                                                MemberAccessExpression
                                                                (
                                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                                    MemberAccessExpression
                                                                    (
                                                                        SyntaxKind.SimpleMemberAccessExpression,
                                                                        IdentifierName("System"),
                                                                        IdentifierName("Runtime")
                                                                    ), IdentifierName("CompilerServices")
                                                                ), IdentifierName("MethodImplOptions")
                                                            ), IdentifierName("AggressiveInlining")
                                                        ),
                                                        CastExpression
                                                        (
                                                            IdentifierName
                                                            (
                                                                "System.Runtime.CompilerServices.MethodImplOptions"
                                                            ), Num(512)
                                                        )
                                                    )
                                                )
                                            )
                                        )
                                    )

                                    #endregion

                                )
                            )
                        )
                    )
                    .WithModifiers(TokenList(Token(SyntaxKind.PrivateKeyword)))
                    .WithBody(Block(body))
#if !DEBUG
                    .WithAttributeLists
                    (
                        SingletonList
                        (
                            AttributeList
                            (
                                SingletonSeparatedList
                                (
                                    Attribute
                                    (
                                        QualifiedName
                                        (
                                            QualifiedName(IdentifierName("System"), IdentifierName("Diagnostics")),
                                            IdentifierName("DebuggerHidden")
                                        )
                                    )
                                )
                            )
                        )
                    )
#endif
                );
                return(IdentifierName(name));
            }
Exemplo n.º 2
0
        private string ProcessClassDeclaration
        (
            ClassDeclarationSyntax classDeclaration,
            GeneratorExecutionContext sourceContext,
            INamedTypeSymbol nativeApiAttributeSymbol,
            MarshalBuilder rootMarshalBuilder,
            ref List <ITypeSymbol> processedSymbols,
            INamedTypeSymbol excludeFromOverrideAttribute
        )
        {
            var stopwatch   = Stopwatch.StartNew();
            var compilation = sourceContext.Compilation;

            if (!classDeclaration.Modifiers.Any(x => x.IsKind(SyntaxKind.PartialKeyword)))
            {
                return(null);
            }

            if (!classDeclaration.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
            {
                return(null);
            }
            var namespaceDeclaration = (NamespaceDeclarationSyntax)classDeclaration.Parent;

            if (!namespaceDeclaration.Parent.IsKind(SyntaxKind.CompilationUnit))
            {
                return(null);
            }

            var compilationUnit = (CompilationUnitSyntax)namespaceDeclaration.Parent;

            var classSymbol = ModelExtensions.GetDeclaredSymbol
                                  (compilation.GetSemanticModel(classDeclaration.SyntaxTree), classDeclaration) as ITypeSymbol;

            if (!compilation.HasImplicitConversion
                    (classSymbol, compilation.GetTypeByMetadataName("Silk.NET.Core.Native.NativeApiContainer")))
            {
                return(null);
            }

            var classIsSealed = classDeclaration.Modifiers.Any(x => x.Text == "sealed");
            var generateSeal  = false;

            if (sourceContext.AnalyzerConfigOptions.GetOptions
                    (classDeclaration.SyntaxTree)
                .TryGetValue("silk_touch_sealed_vtable_creation", out var generateSealstr))
            {
                if (bool.TryParse(generateSealstr, out var v))
                {
                    generateSeal = v;
                }
            }


            var generateVTable = false;

            if (sourceContext.AnalyzerConfigOptions.GetOptions
                    (classDeclaration.SyntaxTree)
                .TryGetValue("silk_touch_vtable_generate", out var genvtablestr))
            {
                if (bool.TryParse(genvtablestr, out var v))
                {
                    generateVTable = v;
                }
            }

            var preloadVTable = false;

            if (sourceContext.AnalyzerConfigOptions.GetOptions
                    (classDeclaration.SyntaxTree)
                .TryGetValue("silk_touch_vtable_preload", out var vtablepreloadstr))
            {
                if (bool.TryParse(vtablepreloadstr, out var v))
                {
                    preloadVTable = v;
                }
            }

            var emitAssert = false;

            if (sourceContext.AnalyzerConfigOptions.GetOptions
                    (classDeclaration.SyntaxTree)
                .TryGetValue("silk_touch_vtable_tree_emit_assert", out var emitAssertStr))
            {
                if (bool.TryParse(emitAssertStr, out var v))
                {
                    emitAssert = v;
                }
            }

            var classAttribute = classSymbol.GetAttributes()
                                 .FirstOrDefault(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, nativeApiAttributeSymbol));

            var classNativeApiAttribute = classAttribute == default
                ? new NativeApiAttribute()
                : ToNativeApiAttribute(classAttribute);

            var newMembers = new List <MemberDeclarationSyntax>();

            int slotCount = 0;
            int gcCount   = 0;

            var generatedVTableName = NameGenerator.Name("GeneratedVTable");
            Dictionary <int, string> entryPoints = new Dictionary <int, string>();
            var processedEntrypoints             = new List <EntryPoint>();

            foreach (var(declaration, symbol, entryPoint, callingConvention) in from declaration in
                     from member in classDeclaration.Members
                     where member.IsKind(SyntaxKind.MethodDeclaration)
                     select(MethodDeclarationSyntax) member
                     let symbol = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)
                                  where symbol is not null
                                  let attribute = ToNativeApiAttribute
                                                  (
                         symbol.GetAttributes()
                         .FirstOrDefault
                             (att => SymbolEqualityComparer.Default.Equals(att.AttributeClass, nativeApiAttributeSymbol))
                                                  )
                                                  where declaration.Modifiers.Any
                                                      (modifier => modifier.IsKind(SyntaxKind.PartialKeyword)) && symbol.PartialImplementationPart is null
                                                  let entryPoint = NativeApiAttribute.GetEntryPoint(attribute, classNativeApiAttribute, symbol.Name)
                                                                   let callingConvention = NativeApiAttribute.GetCallingConvention(attribute, classNativeApiAttribute)
                                                                                           select(declaration, symbol, entryPoint, callingConvention))
            {
                var slot = slotCount++; // even though technically that somehow makes slots defined behavior, THEY ARE NOT
                // SLOTS ARE UNDEFINED BEHAVIOR
                ProcessMethod
                (
                    sourceContext, rootMarshalBuilder, callingConvention, entryPoints, entryPoint, classIsSealed,
                    generateSeal, generateVTable, slot, compilation, symbol, declaration, newMembers,
                    ref gcCount, processedEntrypoints, generatedVTableName
                );
            }

            if (slotCount > 0)
            {
                if (!processedSymbols.Contains(classSymbol))
                {
                    newMembers.Add
                    (
                        MethodDeclaration
                        (
                            List <AttributeListSyntax>(),
                            TokenList(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword)),
                            PredefinedType(Token(SyntaxKind.IntKeyword)), null, Identifier("CoreGetSlotCount"), null,
                            ParameterList(), List <TypeParameterConstraintClauseSyntax>(), null,
                            ArrowExpressionClause
                            (
                                BinaryExpression
                                (
                                    SyntaxKind.AddExpression,
                                    LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(slotCount)),
                                    InvocationExpression
                                    (
                                        MemberAccessExpression
                                        (
                                            SyntaxKind.SimpleMemberAccessExpression, BaseExpression(),
                                            IdentifierName("CoreGetSlotCount")
                                        )
                                    )
                                )
                            ), Token(SyntaxKind.SemicolonToken)
                        )
                    );
                    newMembers.Add
                    (
                        MethodDeclaration
                        (
                            List <AttributeListSyntax>(),
                            TokenList(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword)),
                            PredefinedType(Token(SyntaxKind.IntKeyword)), null, Identifier("CoreGcSlotCount"), null,
                            ParameterList(), List <TypeParameterConstraintClauseSyntax>(), null,
                            ArrowExpressionClause
                            (
                                BinaryExpression
                                (
                                    SyntaxKind.AddExpression,
                                    LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(gcCount)),
                                    InvocationExpression
                                    (
                                        MemberAccessExpression
                                        (
                                            SyntaxKind.SimpleMemberAccessExpression, BaseExpression(),
                                            IdentifierName("CoreGcSlotCount")
                                        )
                                    )
                                )
                            ), Token(SyntaxKind.SemicolonToken)
                        )
                    );
                }

                processedSymbols.Add(classSymbol);
            }

            if (newMembers.Count == 0)
            {
                return(null);
            }

            if (generateVTable && entryPoints.Count > 0)
            {
                newMembers.Add
                (
                    GenerateVTable
                    (
                        preloadVTable, entryPoints, emitAssert,
                        sourceContext.ParseOptions.PreprocessorSymbolNames.Any
                            (x => x == "NETCOREAPP" || x == "NET5" /* SEE INativeContext.cs in Core */),
                        generatedVTableName
                    )
                );
                newMembers.Add
                (
                    MethodDeclaration(IdentifierName("IVTable"), Identifier("CreateVTable"))
                    .WithModifiers
                    (
                        TokenList
                        (
                            generateSeal
                                    ? new[]
                {
                    Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.SealedKeyword),
                    Token(SyntaxKind.OverrideKeyword)
                }
                                    : new[] { Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword) }
                        )
                    )
                    .WithExpressionBody
                    (
                        ArrowExpressionClause
                        (
                            ObjectCreationExpression
                                (IdentifierName(generatedVTableName))
                            .WithArgumentList(ArgumentList())
                        )
                    )
                    .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
                );
            }

            ProcessNativeContextOverrides(processedEntrypoints.ToArray(), ref newMembers, classSymbol, classDeclaration, sourceContext.Compilation, excludeFromOverrideAttribute);

            var newNamespace = namespaceDeclaration.WithMembers
                               (
                List
                (
                    new MemberDeclarationSyntax[]
            {
                classDeclaration.WithMembers
                    (List(newMembers))
                .WithAttributeLists(List <AttributeListSyntax>())
            }
                )
                               )
                               .WithUsings(compilationUnit.Usings);

            var result = newNamespace.NormalizeWhitespace().ToFullString();

            stopwatch.Stop();
            var reportTelemetry = true;

#if !DEBUG
            reportTelemetry = sourceContext.AnalyzerConfigOptions.GlobalOptions.TryGetValue
                                  ("silk_touch_telemetry", out var telstr) && bool.Parse(telstr);
#endif
            if (reportTelemetry)
            {
                sourceContext.ReportDiagnostic
                (
                    Diagnostic.Create
                    (
                        Diagnostics.BuildInfo, classDeclaration.GetLocation(), slotCount, gcCount,
                        stopwatch.ElapsedMilliseconds + "ms"
                    )
                );
            }
            return(result);
        }
Exemplo n.º 3
0
            static IdentifierNameSyntax BuildSubLoad
            (
                ref List <MemberDeclarationSyntax> methods,
                ReadOnlySpan <int> keys,
                IReadOnlyDictionary <int, string> entryPoints,
                bool emitAssert,
                bool preloadVTable,
                bool vNext,
                string generatedThrowHelperInvalidSlot
            )
            {
                var body = new List <StatementSyntax>();
                var name = NameGenerator.Name($"Load_{keys[0]}_{keys[keys.Length - 1]}");

                if (keys.Length % 2 != 0)
                {
                    // uneven, load lowest
                    body.Add
                    (
                        IfStatement
                        (
                            BinaryExpression(SyntaxKind.EqualsExpression, IdentifierName(keyName), Num(keys[0])),
                            ReturnStatement
                                (InvocationExpression(BuildFinalSubLoad(ref methods, keys[0], entryPoints[keys[0]], emitAssert, preloadVTable, vNext), ArgumentList(SeparatedList(new [] { Argument(IdentifierName(keyName)), Argument(IdentifierName("entryPoint")) }))))
                        )
                    );
                    if (keys.Length > 1)
                    {
                        body.Add
                            (ReturnStatement(InvocationExpression(BuildSubLoad(ref methods, keys.Slice(1), entryPoints, emitAssert, preloadVTable, vNext, generatedThrowHelperInvalidSlot), ArgumentList(SeparatedList(new [] { Argument(IdentifierName(keyName)), Argument(IdentifierName("entryPoint")) })))));
                    }
                    else
                    {
                        // throw & return default
                        body.Add
                            (ExpressionStatement(InvocationExpression(IdentifierName(generatedThrowHelperInvalidSlot))));
                        body.Add(ReturnStatement(DefaultExpression(IdentifierName("System.IntPtr"))));
                    }
                }
                else
                {
                    // even, but not one, split.
                    var halfIndex = keys.Length / 2;
                    var lower     = keys.Slice(0, halfIndex);
                    var upper     = keys.Slice(halfIndex);
                    var midKey    = keys[halfIndex];

                    body.Add
                    (
                        IfStatement
                        (
                            BinaryExpression(SyntaxKind.LessThanExpression, IdentifierName(keyName), Num(midKey)),
                            ReturnStatement(InvocationExpression(BuildSubLoad(ref methods, lower, entryPoints, emitAssert, preloadVTable, vNext, generatedThrowHelperInvalidSlot), ArgumentList(SeparatedList(new [] { Argument(IdentifierName(keyName)), Argument(IdentifierName("entryPoint")) })))),
                            ElseClause
                                (ReturnStatement(InvocationExpression(BuildSubLoad(ref methods, upper, entryPoints, emitAssert, preloadVTable, vNext, generatedThrowHelperInvalidSlot), ArgumentList(SeparatedList(new [] { Argument(IdentifierName(keyName)), Argument(IdentifierName("entryPoint")) })))))
                        )
                    );
                }

                methods.Add
                (
                    MethodDeclaration
                        (QualifiedName(IdentifierName("System"), IdentifierName("IntPtr")), Identifier(name))
                    .WithParameterList
                    (
                        ParameterList
                        (
                            SeparatedList
                            (
                                new[]
                {
                    Parameter
                        (Identifier(keyName))
                    .WithType(PredefinedType(Token(SyntaxKind.IntKeyword))),
                    Parameter
                        (Identifier("entryPoint"))
                    .WithType(PredefinedType(Token(SyntaxKind.StringKeyword)))
                }
                            )
                        )
                    )
                    .WithAttributeLists
                    (
                        SingletonList
                        (
                            AttributeList
                            (
                                SingletonSeparatedList
                                (
                                    Attribute
                                    (
                                        QualifiedName
                                        (
                                            QualifiedName
                                            (
                                                QualifiedName
                                                    (IdentifierName("System"), IdentifierName("Runtime")),
                                                IdentifierName("CompilerServices")
                                            ), IdentifierName("MethodImpl")
                                        )
                                    )

                                    #region [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining | System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)]

                                    .WithArgumentList
                                    (
                                        AttributeArgumentList
                                        (
                                            SingletonSeparatedList
                                            (
                                                AttributeArgument
                                                (
                                                    BinaryExpression
                                                    (
                                                        SyntaxKind.BitwiseOrExpression,
                                                        MemberAccessExpression
                                                        (
                                                            SyntaxKind.SimpleMemberAccessExpression,
                                                            MemberAccessExpression
                                                            (
                                                                SyntaxKind.SimpleMemberAccessExpression,
                                                                MemberAccessExpression
                                                                (
                                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                                    MemberAccessExpression
                                                                    (
                                                                        SyntaxKind.SimpleMemberAccessExpression,
                                                                        IdentifierName("System"),
                                                                        IdentifierName("Runtime")
                                                                    ), IdentifierName("CompilerServices")
                                                                ), IdentifierName("MethodImplOptions")
                                                            ), IdentifierName("AggressiveInlining")
                                                        ),
                                                        CastExpression
                                                        (
                                                            IdentifierName
                                                            (
                                                                "System.Runtime.CompilerServices.MethodImplOptions"
                                                            ), Num(512)
                                                        )
                                                    )
                                                )
                                            )
                                        )
                                    )

                                    #endregion

                                )
                            )
                        )
                    )
                    .WithModifiers(TokenList(Token(SyntaxKind.PrivateKeyword)))
                    .WithBody(Block(body))
#if !DEBUG
                    .WithAttributeLists
                    (
                        SingletonList
                        (
                            AttributeList
                            (
                                SingletonSeparatedList
                                (
                                    Attribute
                                    (
                                        QualifiedName
                                        (
                                            QualifiedName(IdentifierName("System"), IdentifierName("Diagnostics")),
                                            IdentifierName("DebuggerHidden")
                                        )
                                    )
                                )
                            )
                        )
                    )
#endif
                );
                return(IdentifierName(name));
            }