Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions src/Godot.SourceGeneration/EntryPointGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,36 +35,32 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Where(spec => spec is not null)
.Select((spec, ct) => spec!.Value)
.Collect()
// Sort for base classes to to be registered before derived classes.
// Sort for base classes to be registered before derived classes.
.Select((specs, ct) =>
{
Dictionary<string, GodotRegistrationSpec> specByTypeName = [];
Dictionary<string, int> derivedTypeCountByBaseTypeName = [];

foreach (var spec in specs)
{
specByTypeName.Add(spec.FullyQualifiedSymbolName, spec);

if (!derivedTypeCountByBaseTypeName.TryGetValue(spec.FullyQualifiedSymbolName, out int derivedTypeCount))
{
derivedTypeCountByBaseTypeName.Add(spec.FullyQualifiedSymbolName, 0);
derivedTypeCount++;
}
specByTypeName[spec.FullyQualifiedSymbolName] = spec;
}

// Number of ancestors (transitively) that are also registered in this assembly.
// A base type therefore has a strictly lower depth than any type deriving from it,
// so ordering by depth guarantees base classes register before derived classes.
int GetRegisteredAncestorDepth(GodotRegistrationSpec spec)
{
int depth = 0;
string baseTypeName = spec.FullyQualifiedBaseSymbolName;
while (specByTypeName.TryGetValue(baseTypeName, out var baseTypeNameType))
while (specByTypeName.TryGetValue(baseTypeName, out var baseSpec))
{
derivedTypeCountByBaseTypeName[baseTypeName] += derivedTypeCount;
baseTypeName = baseTypeNameType.FullyQualifiedBaseSymbolName;
depth++;
baseTypeName = baseSpec.FullyQualifiedBaseSymbolName;
}

derivedTypeCountByBaseTypeName.TryGetValue(baseTypeName, out int oldDerivedTypeCount);
derivedTypeCountByBaseTypeName[baseTypeName] = oldDerivedTypeCount + derivedTypeCount;
return depth;
}

return specByTypeName
.OrderByDescending(kvp => derivedTypeCountByBaseTypeName[kvp.Key])
.Select(kvp => kvp.Value)
return specs
.OrderBy(GetRegisteredAncestorDepth)
.ToImmutableArray();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,13 @@ await Verifier.Verify(
[("Main.generated.cs", "MainWithInheritance.generated.cs")]
);
}

[Fact]
public async Task BaseTypesRegisteredBeforeDerivedTypesWhenDeclaredDerivedFirst()
{
await Verifier.Verify(
["NodesWithInheritanceReversed.cs"],
[("Main.generated.cs", "MainWithInheritanceReversed.generated.cs")]
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ internal static void InitializeUserTypes(global::Godot.Bridge.InitializationLeve
return;
}
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.BaseType>(global::NS.BaseType.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.DerivedType3>(global::NS.DerivedType3.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.DerivedType>(global::NS.DerivedType.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.DerivedType2>(global::NS.DerivedType2.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.DerivedType3>(global::NS.DerivedType3.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.HighlyDerivedType>(global::NS.HighlyDerivedType.BindMembers);
}
internal static void DeinitializeUserTypes(global::Godot.Bridge.InitializationLevel level)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[assembly: global::System.Runtime.CompilerServices.DisableRuntimeMarshalling]

#nullable enable

namespace @TestProject;

static partial class Main
{
internal static void InitializeTypes(global::Godot.Bridge.InitializationLevel level)
{
ClassDBExtensions.InitializeUserTypes(level);
}
internal static void DeinitializeTypes(global::Godot.Bridge.InitializationLevel level)
{
ClassDBExtensions.DeinitializeUserTypes(level);
}
[global::System.Runtime.InteropServices.UnmanagedCallersOnly(EntryPoint = "init")]
internal static bool Init(nint getProcAddress, nint library, nint initialization)
{
global::Godot.Bridge.GodotBridge.Initialize(getProcAddress, library, initialization, config =>
{
config.SetMinimumLibraryInitializationLevel(global::Godot.Bridge.InitializationLevel.Scene);
config.RegisterInitializer(InitializeTypes);
config.RegisterTerminator(DeinitializeTypes);
});
return true;
}
}
internal static class ClassDBExtensions
{
internal static void InitializeUserTypes(global::Godot.Bridge.InitializationLevel level)
{
if (level != global::Godot.Bridge.InitializationLevel.Scene)
{
return;
}
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.Mob>(global::NS.Mob.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.Enemy>(global::NS.Enemy.BindMembers);
global::Godot.Bridge.GodotRegistry.RegisterRuntimeClass<global::NS.Bat>(global::NS.Bat.BindMembers);
}
internal static void DeinitializeUserTypes(global::Godot.Bridge.InitializationLevel level)
{
if (level != global::Godot.Bridge.InitializationLevel.Scene)
{
return;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Godot;
using Godot.Bridge;

namespace NS;

[GodotClass]
public partial class Bat : Enemy
{
public new static void BindMembers(ClassRegistrationContext context) { }
}

[GodotClass]
public partial class Enemy : Mob
{
public new static void BindMembers(ClassRegistrationContext context) { }
}

[GodotClass]
public partial class Mob : Node
{
public static void BindMembers(ClassRegistrationContext context) { }
}