public MarshalBuilder Clone()
        {
            var n = new MarshalBuilder();

            n._middleware.AddRange(_middleware);
            return(n);
        }
Example #2
0
        public void Execute(GeneratorExecutionContext context)
        {
            if (!context.Compilation.ReferencedAssemblyNames.Any
                    (ai => ai.Name.Equals("Silk.NET.Core", StringComparison.OrdinalIgnoreCase)))
            {
                context.ReportDiagnostic(Diagnostic.Create(Diagnostics.SilkNetCoreMissing, Location.None));
                return;
            }

            if (context.SyntaxContextReceiver is not SyntaxReceiver receiver)
            {
                return;
            }

            var nativeApiAttribute = context.Compilation.GetTypeByMetadataName
                                         ("Silk.NET.Core.Native.NativeApiAttribute");

            if (nativeApiAttribute is null)
            {
                return;
            }

            var pInvokeAttribute = context.Compilation.GetTypeByMetadataName("Silk.NET.Core.Native.PInvokeOverride");

            if (pInvokeAttribute is null)
            {
                return;
            }

            var excludeFromOverrideAttribute = context.Compilation.GetTypeByMetadataName
                                                   ("Silk.NET.Core.Native.ExcludeFromOverrideAttribute");

            if (excludeFromOverrideAttribute is null)
            {
                return;
            }

            _nativeContextAttributes[pInvokeAttribute] = array => ((int)array[0].Value !,
                                                                   (string)array[1].Value !/* first return is just the lib target */, new PInvokeNativeContextOverride());


            var marshalBuilder = new MarshalBuilder();

            // begin            |           end
            marshalBuilder.Use(Middlewares.InjectMiddleware);
            marshalBuilder.Use(Middlewares.ParameterInitMiddleware);
            marshalBuilder.Use(Middlewares.StringMarshaller);
            marshalBuilder.Use(Middlewares.PinMiddleware);
            // post init        |           -
            marshalBuilder.Use(Middlewares.SpanMarshaller);
            marshalBuilder.Use(Middlewares.BoolMarshaller);
            marshalBuilder.Use(Middlewares.PinObjectMarshaller);
            // post pin
            marshalBuilder.Use(Middlewares.DelegateMarshaller);
            marshalBuilder.Use(Middlewares.GenericPointerMarshaller);
            // pre load         |           post load

            List <ITypeSymbol> processedSymbols = new List <ITypeSymbol>();


            foreach (var group in receiver.ClassDeclarations.Select(x => (x.Item1, x.Item2, x.Item2.GetDeclaredSymbol(x.Item1)))
                     .GroupBy(x => x.Item3, SymbolEqualityComparer.Default))
            {
                try
                {
                    var s = ProcessClassDeclaration
                            (
                        group.Select(x => (x.Item1, x.Item2)), context, nativeApiAttribute, marshalBuilder, ref processedSymbols,
                        excludeFromOverrideAttribute, (INamedTypeSymbol) group.Key
                            );

                    if (s is not {
                        Length: > 0
                    })
                    {
                        continue;
                    }

                    Output(context, group.Key.Name, s);
                }
Example #3
0
        public void Execute(GeneratorExecutionContext context)
        {
            MarshalBuilder marshalBuilder;

            if (!context.Compilation.ReferencedAssemblyNames.Any
                    (ai => ai.Name.Equals("Silk.NET.Core", StringComparison.OrdinalIgnoreCase)))
            {
                context.ReportDiagnostic(Diagnostic.Create(Diagnostics.SilkNetCoreMissing, Location.None));
                return;
            }

            if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
            {
                return;
            }

            var nativeApiAttribute = context.Compilation.GetTypeByMetadataName
                                         ("Silk.NET.Core.Native.NativeApiAttribute");

            if (nativeApiAttribute is null)
            {
                return;
            }

            var pInvokeAttribute = context.Compilation.GetTypeByMetadataName("Silk.NET.Core.Native.PInvokeOverride");

            if (pInvokeAttribute is null)
            {
                return;
            }

            var excludeFromOverrideAttribute = context.Compilation.GetTypeByMetadataName
                                                   ("Silk.NET.Core.Native.ExcludeFromOverrideAttribute");

            if (excludeFromOverrideAttribute is null)
            {
                return;
            }

            _nativeContextAttributes[pInvokeAttribute] = array => ((int)array[0].Value !,
                                                                   (string)array[1].Value !/* first return is just the lib target */, new PInvokeNativeContextOverride());


            marshalBuilder = new MarshalBuilder();

            marshalBuilder.Use(Middlewares.ParameterInitMiddleware);
            marshalBuilder.Use(Middlewares.StringMarshaller);
            marshalBuilder.Use(Middlewares.PinMiddleware);
            marshalBuilder.Use(Middlewares.SpanMarshaller);
            marshalBuilder.Use(Middlewares.BoolMarshaller);
            marshalBuilder.Use(Middlewares.PinObjectMarshaller);
            marshalBuilder.Use(Middlewares.DelegateMarshaller);
            marshalBuilder.Use(Middlewares.GenericPointerMarshaller);

            List <ITypeSymbol> processedSymbols = new List <ITypeSymbol>();


            foreach (var receiverClassDeclaration in receiver.ClassDeclarations)
            {
                try
                {
                    var s = ProcessClassDeclaration
                            (
                        receiverClassDeclaration, context, nativeApiAttribute, marshalBuilder, ref processedSymbols,
                        excludeFromOverrideAttribute
                            );

                    if (s is null)
                    {
                        continue;
                    }

                    var name =
                        $"{receiverClassDeclaration.Identifier.Text}.{receiverClassDeclaration.GetHashCode()}.gen";
                    context.AddSource(name, SourceText.From(s, Encoding.UTF8));
                    // File.WriteAllText(@"C:\SILK.NET\src\Lab\" + name, s);
                }
                catch (Exception ex)
                {
                    context.ReportDiagnostic
                    (
                        Diagnostic.Create
                            (Diagnostics.ProcessClassFailure, receiverClassDeclaration.GetLocation(), ex.ToString())
                    );
                }
            }
        }
Example #4
0
        private static void ProcessMethod
        (
            GeneratorExecutionContext sourceContext,
            MarshalBuilder rootMarshalBuilder,
            CallingConvention callingConvention,
            Dictionary <int, string> entryPoints,
            string entryPoint,
            bool classIsSealed,
            bool generateSeal,
            bool generateVTable,
            int slot,
            Compilation compilation,
            IMethodSymbol symbol,
            MethodDeclarationSyntax declaration,
            List <MemberDeclarationSyntax> newMembers,
            ref int gcCount,
            List <EntryPoint> processedEntrypoints
        )
        {
            void BuildLoadInvoke(ref IMarshalContext ctx, Action next)
            {
                // this is terminal, we never call next

                var parameters = ctx.ResolveAllLoadParameters();

                var fPtrType = FunctionPointerType
                               (
                    FunctionPointerCallingConvention
                    (
                        Token(SyntaxKind.UnmanagedKeyword),
                        FunctionPointerUnmanagedCallingConventionList
                        (
                            SingletonSeparatedList
                            (
                                FunctionPointerUnmanagedCallingConvention
                                    (Identifier(GetCallingConvention(callingConvention)))
                            )
                        )
                    ),
                    FunctionPointerParameterList
                    (
                        SeparatedList
                        (
                            ctx.LoadTypes.Select
                            (
                                x => FunctionPointerParameter
                                    (IdentifierName(x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))
                            )
                        )
                    )
                               );

                entryPoints[ctx.Slot] = entryPoint;
                processedEntrypoints.Add
                (
                    new EntryPoint
                    (
                        entryPoint, ctx.Slot, callingConvention,
                        ctx.LoadTypes.Select
                        (
                            x => (TypeSyntax)IdentifierName
                                (x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
                        )
                        .ToArray(),
                        symbol
                    )
                );

                ExpressionSyntax loadCallTarget;

                if ((classIsSealed || generateSeal) && generateVTable)
                {
                    loadCallTarget = MemberAccessExpression
                                     (
                        SyntaxKind.SimpleMemberAccessExpression,
                        ParenthesizedExpression
                        (
                            BinaryExpression
                            (
                                SyntaxKind.AsExpression, IdentifierName("CurrentVTable"), IdentifierName("GeneratedVTable")
                            )
                        ), IdentifierName("Load")
                                     );
                }
                else
                {
                    loadCallTarget = IdentifierName("Load");
                }

                // build load + invocation
                Func <IMarshalContext, ExpressionSyntax> expression = ctx => InvocationExpression
                                                                      (
                    ParenthesizedExpression
                    (
                        CastExpression
                        (
                            fPtrType, InvocationExpression
                            (
                                loadCallTarget, ArgumentList
                                (
                                    SeparatedList
                                    (
                                        new[]
                {
                    Argument
                        (LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ctx.Slot))),
                    Argument
                        (LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(entryPoint)))
                }
                                    )
                                )
                            )
                        )
                    ), ArgumentList(SeparatedList(parameters.Select(x => Argument(x.Value))))
                                                                      );

                if (ctx.ReturnsVoid)
                {
                    var exp = expression(ctx); // this forces evaluation of everything until this point.
                    ctx.AddSideEffect(ctx => ExpressionStatement(exp));
                    ctx.ResultVariable = null;
                }
                else
                {
                    var id = ctx.DeclareVariable(ctx.ReturnLoadType, false);
                    ctx.ResultVariable = id;
                    ctx.SetVariable(id, expression);
                    _ = ctx.ResolveVariable(id).Value; // force evaluation of ret
                }

                ctx.CurrentResultType = ctx.ReturnLoadType;
            }

            try
            {
                var marshalBuilder = rootMarshalBuilder.Clone();

                marshalBuilder.Use(BuildLoadInvoke);

                var context = new MarshalContext(compilation, symbol, slot);

                marshalBuilder.Run(context);

                var block = context.BuildFinalBlock();

                gcCount += context.GCCount;

                if (declaration.Modifiers.All(x => x.Text != "unsafe"))
                {
                    // this is not done as a middleware to allow middleware to prepend any variable declaration, even if it's unsafe
                    block = Block(UnsafeStatement(Token(SyntaxKind.UnsafeKeyword), block));
                }

                var method = declaration.WithBody
                                 (block)
                             .WithAttributeLists(default)
Example #5
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;

            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
                );
            }

            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 */)
                    )
                );
                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("GeneratedVTable"))
                            .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);
        }