mirror of
https://github.com/godotengine/godot.git
synced 2024-12-27 11:24:59 +08:00
4d710bf659
This replaces the way we invoke methods and set/get properties. This first iteration rids us of runtime type checking in those cases, as it's now done at compile time. Later it will also stop needing the use of reflection. After that, we will only depend on reflection for generic Godot Array and Dictionary. We're stuck with reflection in generic collections for now as C# doesn't support generic/template specialization. This is only the initial implementation. Further iterations are coming, specially once we switch to the native extension system which completely changes the way members are accessed/invoked. For example, with the native extension system we will likely need to create `UnmanagedCallersOnly` invoke wrapper methods and return function pointers to the engine. Other kind of members, like event signals will be receiving the same treatment in the future.
92 lines
3.1 KiB
C#
92 lines
3.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
namespace Godot.SourceGenerators
|
|
{
|
|
static class ExtensionMethods
|
|
{
|
|
public static bool TryGetGlobalAnalyzerProperty(
|
|
this GeneratorExecutionContext context, string property, out string? value
|
|
) => context.AnalyzerConfigOptions.GlobalOptions
|
|
.TryGetValue("build_property." + property, out value);
|
|
|
|
public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context)
|
|
=> context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) &&
|
|
toggle != null &&
|
|
toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase);
|
|
|
|
private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
|
|
{
|
|
if (symbol == null)
|
|
return false;
|
|
|
|
while (true)
|
|
{
|
|
if (symbol.ToString() == baseName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (symbol.BaseType != null)
|
|
{
|
|
symbol = symbol.BaseType;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsGodotScriptClass(
|
|
this ClassDeclarationSyntax cds, Compilation compilation,
|
|
out INamedTypeSymbol? symbol
|
|
)
|
|
{
|
|
var sm = compilation.GetSemanticModel(cds.SyntaxTree);
|
|
|
|
var classTypeSymbol = sm.GetDeclaredSymbol(cds);
|
|
|
|
if (classTypeSymbol?.BaseType == null
|
|
|| !classTypeSymbol.BaseType.InheritsFrom(GodotClasses.Object))
|
|
{
|
|
symbol = null;
|
|
return false;
|
|
}
|
|
|
|
symbol = classTypeSymbol;
|
|
return true;
|
|
}
|
|
|
|
public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectGodotScriptClasses(
|
|
this IEnumerable<ClassDeclarationSyntax> source,
|
|
Compilation compilation
|
|
)
|
|
{
|
|
foreach (var cds in source)
|
|
{
|
|
if (cds.IsGodotScriptClass(compilation, out var symbol))
|
|
yield return (cds, symbol!);
|
|
}
|
|
}
|
|
|
|
public static bool IsPartial(this ClassDeclarationSyntax cds)
|
|
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
|
|
|
|
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
|
|
SymbolDisplayFormat.FullyQualifiedFormat
|
|
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
|
|
|
|
public static string FullQualifiedName(this ITypeSymbol symbol)
|
|
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
|
|
|
|
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
|
|
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
|
|
}
|
|
}
|