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