mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
More generating script glue
This commit is contained in:
@@ -66,6 +66,17 @@ struct TypeTranslationTemplate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AttributeUsage(.Parameter)]
|
||||||
|
struct GlueParamAttribute : Attribute
|
||||||
|
{
|
||||||
|
public String WrapperType;
|
||||||
|
|
||||||
|
public this(String wrapperType)
|
||||||
|
{
|
||||||
|
WrapperType = wrapperType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[AttributeUsage(.Struct)]
|
[AttributeUsage(.Struct)]
|
||||||
struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
||||||
{
|
{
|
||||||
@@ -74,7 +85,22 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
("int32", "int"),
|
("int32", "int"),
|
||||||
("char16*", "char*"),
|
("char16*", "char*"),
|
||||||
("char8*", "byte*"),
|
("char8*", "byte*"),
|
||||||
("GlitchyEngine.Events.MouseButton", "GlitchyEngine.MouseButton")
|
("uint8*", "byte*"),
|
||||||
|
("GlitchyEngine.Events.MouseButton", "GlitchyEngine.MouseButton"),
|
||||||
|
("GlitchLog.LogLevel", "Log.LogLevel"),
|
||||||
|
("out GlitchyEngine.Content.AssetHandle", "UUID"),
|
||||||
|
("GlitchyEngine.Content.AssetHandle", "UUID"),
|
||||||
|
("out GlitchyEngine.Renderer.Text.FontRenderer.HorizontalTextAlignment", "HorizontalTextAlignment"),
|
||||||
|
("GlitchyEngine.Renderer.Text.FontRenderer.HorizontalTextAlignment", "HorizontalTextAlignment"),
|
||||||
|
("GlitchyEngine.Renderer.ShaderVariableType", "Material.ShaderVariableType"),
|
||||||
|
("in GlitchyEngine.Math.Quaternion", "in System.Numerics.Quaternion"),
|
||||||
|
("out GlitchyEngine.Math.Quaternion", "out System.Numerics.Quaternion"),
|
||||||
|
("out GlitchyEngine.Scripting.ScriptGlue.AxisAngle", "out RotationAxisAngle"),
|
||||||
|
("GlitchyEngine.Scripting.ScriptGlue.AxisAngle", "RotationAxisAngle"),
|
||||||
|
("out GlitchyEngine.World.Rigidbody2DComponent.BodyType", "out BodyType"),
|
||||||
|
("in GlitchyEngine.World.Rigidbody2DComponent.BodyType", "in BodyType"),
|
||||||
|
("out GlitchyEngine.World.SceneCamera.ProjectionType", "out ProjectionType"),
|
||||||
|
("in GlitchyEngine.World.SceneCamera.ProjectionType", "in ProjectionType"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// {0}... Out name,
|
// {0}... Out name,
|
||||||
@@ -158,6 +184,12 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
|
|
||||||
TypeTranslationTemplate template = GetCSharpWrapperTemplate(beefParameterType);
|
TypeTranslationTemplate template = GetCSharpWrapperTemplate(beefParameterType);
|
||||||
|
|
||||||
|
// TODO: This is currently not implemented in beef.
|
||||||
|
/*if (method.GetParamCustomAttribute<GlueParamAttribute>(i) case .Ok(let attribute))
|
||||||
|
{
|
||||||
|
template.PrettyCSharpParamType = attribute.WrapperType;
|
||||||
|
}*/
|
||||||
|
|
||||||
StringView paramName = method.GetParamName(i);
|
StringView paramName = method.GetParamName(i);
|
||||||
|
|
||||||
// Generate the parameter for the wrapper head.
|
// Generate the parameter for the wrapper head.
|
||||||
@@ -202,7 +234,7 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
TypeTranslationTemplate template = GetCSharpReturnValueTemplate(csharpReturnType);
|
TypeTranslationTemplate template = GetCSharpReturnValueTemplate(csharpReturnType);
|
||||||
|
|
||||||
outString.AppendF($"""
|
outString.AppendF($"""
|
||||||
internal static unsafe {template.PrettyCSharpParamType} NEW_{method.Name}({wrapperParameters})
|
internal static unsafe {template.PrettyCSharpParamType} {method.Name}({wrapperParameters})
|
||||||
{{
|
{{
|
||||||
{translation}
|
{translation}
|
||||||
{template.StartCode}_engineFunctions.{method.Name}({callArguments});
|
{template.StartCode}_engineFunctions.{method.Name}({callArguments});
|
||||||
@@ -215,6 +247,48 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String GetTypeInfo(Type type)
|
||||||
|
{
|
||||||
|
String value = new .();
|
||||||
|
type.ToString(value);
|
||||||
|
|
||||||
|
if (type.IsEnum)
|
||||||
|
{
|
||||||
|
value.AppendF($" : {type.UnderlyingType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateJsonInfo(MethodInfo method, String outString)
|
||||||
|
{
|
||||||
|
String parameters = new String();
|
||||||
|
|
||||||
|
for (int i < method.ParamCount)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
parameters.Append(", ");
|
||||||
|
|
||||||
|
parameters.AppendF($"""
|
||||||
|
|
||||||
|
{{
|
||||||
|
"name": "{method.GetParamName(i)}",
|
||||||
|
"type": "{GetTypeInfo(method.GetParamType(i))}"
|
||||||
|
}}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
outString.AppendF($"""
|
||||||
|
|
||||||
|
{{
|
||||||
|
"name": "{method.Name}",
|
||||||
|
"return_type": "{GetTypeInfo(method.ReturnType)}",
|
||||||
|
"parameters": [{parameters}
|
||||||
|
]
|
||||||
|
}},
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
[Comptime]
|
[Comptime]
|
||||||
public void ApplyToType(Type self)
|
public void ApplyToType(Type self)
|
||||||
{
|
{
|
||||||
@@ -223,6 +297,10 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using GlitchyEngine;
|
using GlitchyEngine;
|
||||||
using GlitchyEngine.Core;
|
using GlitchyEngine.Core;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine.Physics;
|
||||||
|
using GlitchyEngine.Graphics;
|
||||||
|
using GlitchyEngine.Graphics.Text;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
@@ -239,6 +317,8 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
|
String jsonOutput = new String("[");
|
||||||
|
|
||||||
for (MethodInfo methodInfo in typeof(ScriptGlue).GetMethods(.Static | .NonPublic))
|
for (MethodInfo methodInfo in typeof(ScriptGlue).GetMethods(.Static | .NonPublic))
|
||||||
{
|
{
|
||||||
if (methodInfo.GetCustomAttribute<RegisterCallAttribute>() case .Ok(let attribute))
|
if (methodInfo.GetCustomAttribute<RegisterCallAttribute>() case .Ok(let attribute))
|
||||||
@@ -260,6 +340,7 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
GenerateCSharpFunctionPointer(methodInfo, csharpEngineFunctionsStruct);
|
GenerateCSharpFunctionPointer(methodInfo, csharpEngineFunctionsStruct);
|
||||||
|
|
||||||
GenerateCSharpWrapperMethod(methodInfo, csharpScriptGlue);
|
GenerateCSharpWrapperMethod(methodInfo, csharpScriptGlue);
|
||||||
|
GenerateJsonInfo(methodInfo, jsonOutput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +349,21 @@ struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
|||||||
|
|
||||||
csharpEngineFunctionsStruct.Append(csharpScriptGlue);
|
csharpEngineFunctionsStruct.Append(csharpScriptGlue);
|
||||||
|
|
||||||
if (File.WriteAllText("../ScriptCore/ScriptGlue.gen.cs", csharpEngineFunctionsStruct) case .Err)
|
/*if (File.WriteAllText("../ScriptCore/ScriptGlue.gen.cs", csharpEngineFunctionsStruct) case .Err)
|
||||||
|
{
|
||||||
|
Runtime.FatalError("Failed to write file");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (jsonOutput.EndsWith(","))
|
||||||
|
{
|
||||||
|
jsonOutput.RemoveFromEnd(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonOutput.Append(']');
|
||||||
|
|
||||||
|
Directory.CreateDirectory("../generated");
|
||||||
|
|
||||||
|
if (File.WriteAllText("../generated/ScriptGlue.json", jsonOutput) case .Err)
|
||||||
{
|
{
|
||||||
Runtime.FatalError("Failed to write file");
|
Runtime.FatalError("Failed to write file");
|
||||||
}
|
}
|
||||||
@@ -537,7 +632,7 @@ static class ScriptGlue
|
|||||||
|
|
||||||
[RegisterCall("ScriptGlue::Log_LogMessage")]
|
[RegisterCall("ScriptGlue::Log_LogMessage")]
|
||||||
[CallingConvention(.Cdecl)]
|
[CallingConvention(.Cdecl)]
|
||||||
static void Log_LogMessage(int32 logLevel, char16* messagePtr, char16* fileNamePtr, int lineNumber)
|
static void Log_LogMessage(LogLevel logLevel, char16* messagePtr, char16* fileNamePtr, int lineNumber)
|
||||||
{
|
{
|
||||||
String escapedMessage = new:ScopedAlloc! String(messagePtr);
|
String escapedMessage = new:ScopedAlloc! String(messagePtr);
|
||||||
|
|
||||||
@@ -1421,14 +1516,14 @@ static class ScriptGlue
|
|||||||
}
|
}
|
||||||
|
|
||||||
[RegisterCall("ScriptGlue::TextRenderer_GetHorizontalAlignment")]
|
[RegisterCall("ScriptGlue::TextRenderer_GetHorizontalAlignment")]
|
||||||
static void TextRenderer_GetHorizontalAlignment(UUID entityId, out HorizontalTextAlignment horizontalAlignment)
|
static void TextRenderer_GetHorizontalAlignment(UUID entityId, [GlueParam("out HorizontalTextAlignment")] out HorizontalTextAlignment horizontalAlignment)
|
||||||
{
|
{
|
||||||
TextRendererComponent* textComponent = GetComponentSafe<TextRendererComponent>(entityId);
|
TextRendererComponent* textComponent = GetComponentSafe<TextRendererComponent>(entityId);
|
||||||
horizontalAlignment = textComponent.HorizontalAlignment;
|
horizontalAlignment = textComponent.HorizontalAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RegisterCall("ScriptGlue::TextRenderer_SetHorizontalAlignment")]
|
[RegisterCall("ScriptGlue::TextRenderer_SetHorizontalAlignment")]
|
||||||
static void TextRenderer_SetHorizontalAlignment(UUID entityId, HorizontalTextAlignment horizontalAlignment)
|
static void TextRenderer_SetHorizontalAlignment(UUID entityId, [GlueParam("HorizontalTextAlignment")] HorizontalTextAlignment horizontalAlignment)
|
||||||
{
|
{
|
||||||
TextRendererComponent* textComponent = GetComponentSafe<TextRendererComponent>(entityId);
|
TextRendererComponent* textComponent = GetComponentSafe<TextRendererComponent>(entityId);
|
||||||
textComponent.HorizontalAlignment = horizontalAlignment;
|
textComponent.HorizontalAlignment = horizontalAlignment;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Core;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true)]
|
||||||
|
public class EngineClassAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string EngineClassName { get; }
|
||||||
|
|
||||||
|
public EngineClassAttribute(string engineClassName)
|
||||||
|
{
|
||||||
|
EngineClassName = engineClassName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ namespace GlitchyEngine.Core;
|
|||||||
/// Represents a universally unique identifier (UUID).
|
/// Represents a universally unique identifier (UUID).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[EngineClass("GlitchyEngine.Core.UUID")]
|
||||||
|
[EngineClass("GlitchyEngine.Core.AssetHandle")]
|
||||||
public struct UUID
|
public struct UUID
|
||||||
{
|
{
|
||||||
private ulong _uuid;
|
private ulong _uuid;
|
||||||
|
|||||||
@@ -9,11 +9,19 @@ namespace GlitchyEngine.Graphics;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Material : Asset
|
public class Material : Asset
|
||||||
{
|
{
|
||||||
|
internal enum ShaderVariableType : byte
|
||||||
|
{
|
||||||
|
Bool,
|
||||||
|
Float,
|
||||||
|
Int,
|
||||||
|
UInt
|
||||||
|
}
|
||||||
|
|
||||||
public void SetVariable(string name, float4 value)
|
public void SetVariable(string name, float4 value)
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
ScriptGlue.Material_SetVariable(_uuid, name, ScriptGlue.ShaderVariableType.Float, 1, 4, 1, &value, sizeof(float4));
|
ScriptGlue.Material_SetVariable(_uuid, name, ShaderVariableType.Float, 1, 4, 1, &value, sizeof(float4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using GlitchyEngine.Core;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
@@ -12,7 +13,8 @@ public class Log
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The severity of the log message.
|
/// The severity of the log message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal enum LogLevel
|
[EngineClass("GlitchLog.LogLevel")]
|
||||||
|
internal enum LogLevel : byte
|
||||||
{
|
{
|
||||||
Trace = 0,
|
Trace = 0,
|
||||||
Debug,
|
Debug,
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
using GlitchyEngine.Core;
|
||||||
|
|
||||||
namespace GlitchyEngine;
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An enum of all mouse buttons that can be queried using methods in <see cref="Input"/>.
|
/// An enum of all mouse buttons that can be queried using methods in <see cref="Input"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[EngineClass("GlitchyEngine.Events.MouseButton")]
|
||||||
public enum MouseButton : byte
|
public enum MouseButton : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
<!--<DebugType>embedded</DebugType>-->
|
<!--<DebugType>embedded</DebugType>-->
|
||||||
<!--<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>-->
|
<!--<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>-->
|
||||||
|
<GeneratedSchemaPath>$(ProjectDir)../generated/ScriptGlue.json</GeneratedSchemaPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
<Exec Command="PowerShell ./postbuild.ps1 -sourceDir $(OutDir) -destinationDir "..\GlitchyEditor\resources\scripts"" />
|
<Exec Command="PowerShell ./postbuild.ps1 -sourceDir $(OutDir) -destinationDir "..\GlitchyEditor\resources\scripts"" />
|
||||||
@@ -27,4 +28,7 @@
|
|||||||
<ProjectReference Include="..\ScriptCoreGenerator\ScriptCoreGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\ScriptCoreGenerator\ScriptCoreGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
<ProjectReference Include="..\vendor\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj" />
|
<ProjectReference Include="..\vendor\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="$(GeneratedSchemaPath)" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+23
-21
@@ -25,7 +25,7 @@ namespace GlitchyEngine;
|
|||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal unsafe partial struct EngineFunctions
|
internal unsafe partial struct EngineFunctions
|
||||||
{
|
{
|
||||||
public delegate* unmanaged[Cdecl]<Log.LogLevel, char*, char*, int, void> Log_LogMessage;
|
public delegate* unmanaged[Cdecl]<GlitchyEngine.Log.LogLevel, char*, char*, int, void> Log_LogMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -471,21 +471,31 @@ internal static partial class ScriptGlue
|
|||||||
|
|
||||||
#region Log
|
#region Log
|
||||||
|
|
||||||
internal static unsafe void Log_LogMessage(Log.LogLevel logLevel, string message, string filePath, int line)
|
// public unsafe static void Log_LogMessage(GlitchyEngine.Log.LogLevel logLevel, string messagePtr, string fileNamePtr, int lineNumber)
|
||||||
{
|
|
||||||
// unsafe
|
|
||||||
// {
|
// {
|
||||||
// byte* mess = (byte*)Marshal.StringToCoTaskMemUTF8(message);
|
// fixed (char* messagePtrConverted = messagePtr) {
|
||||||
//
|
// fixed (char* fileNamePtrConverted = fileNamePtr) {
|
||||||
// fixed (char* messagePtr = message)
|
// _engineFunctions.Log_LogMessage(logLevel, messagePtrConverted, fileNamePtrConverted, lineNumber);
|
||||||
// fixed (char* filePathPtr = filePath)
|
// }
|
||||||
// {
|
|
||||||
// _engineFunctions.Log_LogMessage(logLevel, messagePtr, filePathPtr, line);
|
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Marshal.FreeCoTaskMem((IntPtr)mess);
|
//
|
||||||
// }
|
// }
|
||||||
}
|
//internal static unsafe void Log_LogMessage(Log.LogLevel logLevel, string message, string filePath, int line)
|
||||||
|
//{
|
||||||
|
// // unsafe
|
||||||
|
// // {
|
||||||
|
// // byte* mess = (byte*)Marshal.StringToCoTaskMemUTF8(message);
|
||||||
|
// //
|
||||||
|
// // fixed (char* messagePtr = message)
|
||||||
|
// // fixed (char* filePathPtr = filePath)
|
||||||
|
// // {
|
||||||
|
// // _engineFunctions.Log_LogMessage(logLevel, messagePtr, filePathPtr, line);
|
||||||
|
// // }
|
||||||
|
// //
|
||||||
|
// // Marshal.FreeCoTaskMem((IntPtr)mess);
|
||||||
|
// // }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
@@ -858,16 +868,8 @@ internal static partial class ScriptGlue
|
|||||||
|
|
||||||
#region Material
|
#region Material
|
||||||
|
|
||||||
public enum ShaderVariableType : byte
|
|
||||||
{
|
|
||||||
Bool,
|
|
||||||
Float,
|
|
||||||
Int,
|
|
||||||
UInt
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern unsafe bool Material_SetVariable(UUID assetId, string variableName, ShaderVariableType elementType, int rows, int columns, int arrayLength, void* rawData, int dataLength);
|
internal static extern unsafe bool Material_SetVariable(UUID assetId, string variableName, Material.ShaderVariableType elementType, int rows, int columns, int arrayLength, void* rawData, int dataLength);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern bool Material_ResetVariable(UUID assetId, string variableName);
|
internal static extern bool Material_ResetVariable(UUID assetId, string variableName);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,13 +4,18 @@
|
|||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<IsRoslynComponent>true</IsRoslynComponent>
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<!-- <GeneratedSchemaPath>$(ProjectDir)../generated/ScriptGlue.json</GeneratedSchemaPath>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="System.Text.Json" Version="9.0.7" GeneratePathProperty="true" PrivateAssets="all" />
|
||||||
|
|
||||||
|
<!-- <AdditionalFiles Include="$(GeneratedSchemaPath)" />-->
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,417 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
|
namespace ScriptCoreGenerator;
|
||||||
|
|
||||||
|
internal class GlueMethod
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("return_type")]
|
||||||
|
public string ReturnType { get; set; }
|
||||||
|
[JsonPropertyName("parameters")]
|
||||||
|
public List<Parameter> Parameters { get; set; }
|
||||||
|
|
||||||
|
internal class Parameter
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Generator]
|
||||||
|
public class ScriptGlueGenerator : IIncrementalGenerator
|
||||||
|
{
|
||||||
|
public class GeneratorInput
|
||||||
|
{
|
||||||
|
public ImmutableArray<ITypeSymbol?> TypeMappings;
|
||||||
|
public ImmutableArray<string> GlueFunctionDefinitions;
|
||||||
|
public ImmutableArray<string> ExistingScriptGlueClasses;
|
||||||
|
public ImmutableArray<string> ExistingEngineFunctionStructs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
|
{
|
||||||
|
var mappedTypes = context.SyntaxProvider
|
||||||
|
.CreateSyntaxProvider(
|
||||||
|
CouldBeMappedType, GetTypeOrNull)
|
||||||
|
.Where(type => type is not null)
|
||||||
|
.Collect();
|
||||||
|
|
||||||
|
var manualScriptGlueWrappers = context.SyntaxProvider
|
||||||
|
.CreateSyntaxProvider(
|
||||||
|
IsScriptGlueClass, GetSymbol<ClassDeclarationSyntax>)
|
||||||
|
.Where(type => type is not null)
|
||||||
|
.SelectMany((type, _) => type!.MemberNames)
|
||||||
|
.Collect();
|
||||||
|
|
||||||
|
var manualEngineFunctions = context.SyntaxProvider
|
||||||
|
.CreateSyntaxProvider(
|
||||||
|
IsEngineFunctionsStruct, GetSymbol<StructDeclarationSyntax>)
|
||||||
|
.Where(type => type is not null)
|
||||||
|
.SelectMany((type, _) => type!.MemberNames)
|
||||||
|
.Collect();
|
||||||
|
|
||||||
|
var glueFunctions = context.AdditionalTextsProvider
|
||||||
|
.Where(text => text.Path.EndsWith("ScriptGlue.json",
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Select((text, token) => text.GetText(token)?.ToString())
|
||||||
|
.Where(text => text is not null)!
|
||||||
|
.Collect<string>();
|
||||||
|
|
||||||
|
// Kombiniere zu einem sauberen Record
|
||||||
|
var combinedProvider = mappedTypes
|
||||||
|
.Combine(manualScriptGlueWrappers)
|
||||||
|
.Combine(manualEngineFunctions)
|
||||||
|
.Combine(glueFunctions)
|
||||||
|
.Select((input, _) => new GeneratorInput{
|
||||||
|
TypeMappings = input.Left.Left.Left,
|
||||||
|
ExistingScriptGlueClasses = input.Left.Left.Right,
|
||||||
|
ExistingEngineFunctionStructs = input.Left.Right,
|
||||||
|
GlueFunctionDefinitions = input.Right
|
||||||
|
});
|
||||||
|
|
||||||
|
context.RegisterSourceOutput(combinedProvider, GenerateCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MappedType
|
||||||
|
{
|
||||||
|
public string BeefTypeName;
|
||||||
|
public string? UnderlyingEnumTypeName;
|
||||||
|
public bool HasUnderlyingType => UnderlyingEnumTypeName is not null;
|
||||||
|
|
||||||
|
private string _cSharpTypeName;
|
||||||
|
private string _cSharpWrapperType;
|
||||||
|
|
||||||
|
public MappedType? CSharpUnderlyingEnumType;
|
||||||
|
|
||||||
|
public string? WrapperConvertInput;
|
||||||
|
public string? WrapperCleanupInput;
|
||||||
|
|
||||||
|
public string? ReturnValueConversion = "return returnValue;";
|
||||||
|
|
||||||
|
public string CSharpWrapperType
|
||||||
|
{
|
||||||
|
get => _cSharpWrapperType ?? CSharpTypeName;
|
||||||
|
set => _cSharpWrapperType = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CSharpTypeName
|
||||||
|
{
|
||||||
|
get => _cSharpTypeName ?? BeefTypeName;
|
||||||
|
set => _cSharpTypeName = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MappedType DecodeType(string beefType, Dictionary<string, MappedType> beefTypeToMappedType)
|
||||||
|
{
|
||||||
|
if (beefTypeToMappedType.TryGetValue(beefType, out MappedType? mappedType))
|
||||||
|
{
|
||||||
|
return mappedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colonIndex = beefType.IndexOf(':');
|
||||||
|
bool isEnum = colonIndex != -1;
|
||||||
|
|
||||||
|
string beefTypeName = beefType;
|
||||||
|
string? underlyingEnumType = null;
|
||||||
|
|
||||||
|
if (isEnum)
|
||||||
|
{
|
||||||
|
beefTypeName = beefType.Substring(0, colonIndex).Trim();
|
||||||
|
underlyingEnumType = beefType.Substring(colonIndex + 1).Trim();
|
||||||
|
|
||||||
|
if (beefTypeToMappedType.TryGetValue(beefTypeName, out mappedType))
|
||||||
|
{
|
||||||
|
if (mappedType.UnderlyingEnumTypeName is null)
|
||||||
|
{
|
||||||
|
mappedType.UnderlyingEnumTypeName = underlyingEnumType;
|
||||||
|
mappedType.CSharpUnderlyingEnumType = DecodeType(underlyingEnumType, beefTypeToMappedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MappedType newMapping = new MappedType()
|
||||||
|
{
|
||||||
|
BeefTypeName = beefTypeName,
|
||||||
|
UnderlyingEnumTypeName = underlyingEnumType,
|
||||||
|
CSharpUnderlyingEnumType = underlyingEnumType == null ? null : DecodeType(underlyingEnumType,beefTypeToMappedType),
|
||||||
|
};
|
||||||
|
|
||||||
|
beefTypeToMappedType[beefType] = newMapping;
|
||||||
|
beefTypeToMappedType[beefTypeName] = newMapping;
|
||||||
|
|
||||||
|
return newMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InitializeTypes(Dictionary<string, MappedType> beefTypeToMappedType)
|
||||||
|
{
|
||||||
|
beefTypeToMappedType.Add("void", new MappedType
|
||||||
|
{
|
||||||
|
BeefTypeName = "void",
|
||||||
|
ReturnValueConversion = null
|
||||||
|
});
|
||||||
|
|
||||||
|
beefTypeToMappedType.Add("char16*", new MappedType
|
||||||
|
{
|
||||||
|
BeefTypeName = "char16*",
|
||||||
|
CSharpTypeName = "char*",
|
||||||
|
CSharpWrapperType = "string",
|
||||||
|
ReturnValueConversion = null, // TODO!
|
||||||
|
WrapperConvertInput = "fixed (char* {0} = {1}) {{",
|
||||||
|
WrapperCleanupInput = "}}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateFunctionPointer(GlueMethod method, Dictionary<string, MappedType> beefTypeToMappedType, StringBuilder output)
|
||||||
|
{
|
||||||
|
MappedType returnType = DecodeType(method.ReturnType, beefTypeToMappedType);
|
||||||
|
|
||||||
|
List<MappedType> parameters = new();
|
||||||
|
StringBuilder parameterText = new();
|
||||||
|
|
||||||
|
foreach (var param in method.Parameters)
|
||||||
|
{
|
||||||
|
MappedType parameterType = DecodeType(param.Type, beefTypeToMappedType);
|
||||||
|
parameters.Add(parameterType);
|
||||||
|
|
||||||
|
parameterText.Append(parameterType.CSharpTypeName);
|
||||||
|
parameterText.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.AppendLine($" public delegate* unmanaged[Cdecl]<{parameterText}{returnType.CSharpTypeName}> {method.Name};");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateWrapper(GlueMethod method, Dictionary<string, MappedType> beefTypeToMappedType, StringBuilder output)
|
||||||
|
{
|
||||||
|
// MappedType returnType = DecodeType(method.ReturnType, beefTypeToMappedType);
|
||||||
|
//
|
||||||
|
List<MappedType> parameters = new();
|
||||||
|
StringBuilder parameterText = new();
|
||||||
|
StringBuilder parameterConversion = new();
|
||||||
|
StringBuilder cleanup = new();
|
||||||
|
StringBuilder call = new();
|
||||||
|
|
||||||
|
MappedType returnType = DecodeType(method.ReturnType, beefTypeToMappedType);
|
||||||
|
|
||||||
|
if (returnType.ReturnValueConversion is not null)
|
||||||
|
{
|
||||||
|
call.Append("var returnValue = ");
|
||||||
|
}
|
||||||
|
|
||||||
|
call.Append($"_engineFunctions.{method.Name}(");
|
||||||
|
|
||||||
|
foreach (var param in method.Parameters)
|
||||||
|
{
|
||||||
|
MappedType parameterType = DecodeType(param.Type, beefTypeToMappedType);
|
||||||
|
parameters.Add(parameterType);
|
||||||
|
|
||||||
|
if (parameterText.Length > 0)
|
||||||
|
{
|
||||||
|
parameterText.Append(", ");
|
||||||
|
call.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
parameterText.Append($"{parameterType.CSharpWrapperType} {param.Name}");
|
||||||
|
|
||||||
|
if (parameterType.WrapperConvertInput is not null)
|
||||||
|
{
|
||||||
|
string convertedParamName = $"{param.Name}Converted";
|
||||||
|
|
||||||
|
parameterConversion.AppendLine();
|
||||||
|
parameterConversion.Append("\t\t");
|
||||||
|
parameterConversion.AppendFormat(parameterType.WrapperConvertInput, convertedParamName, param.Name);
|
||||||
|
|
||||||
|
if (parameterType.WrapperCleanupInput is not null)
|
||||||
|
{
|
||||||
|
cleanup.Append("\t\t");
|
||||||
|
cleanup.AppendFormat(parameterType.WrapperCleanupInput, convertedParamName);
|
||||||
|
cleanup.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
call.Append(convertedParamName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
call.Append(param.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
call.Append(");");
|
||||||
|
|
||||||
|
output.AppendLine($$"""
|
||||||
|
public static {{returnType.CSharpWrapperType}} {{method.Name}}({{parameterText}})
|
||||||
|
{{{parameterConversion}}
|
||||||
|
{{call}}
|
||||||
|
{{cleanup}}
|
||||||
|
{{returnType.ReturnValueConversion}}
|
||||||
|
}
|
||||||
|
|
||||||
|
""");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateCode(
|
||||||
|
SourceProductionContext context,
|
||||||
|
GeneratorInput input)
|
||||||
|
{
|
||||||
|
Dictionary<string, MappedType> beefTypeToMappedType = new();
|
||||||
|
|
||||||
|
InitializeTypes(beefTypeToMappedType);
|
||||||
|
|
||||||
|
foreach (ITypeSymbol? mapping in input.TypeMappings)
|
||||||
|
{
|
||||||
|
if (mapping is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string csharpTypeName = mapping.ToDisplayString();
|
||||||
|
|
||||||
|
foreach (AttributeData attribute in mapping.GetAttributes()
|
||||||
|
.Where(attribute => attribute.AttributeClass?.Name == "EngineClassAttribute"))
|
||||||
|
{
|
||||||
|
string? beefTypeName = attribute.ConstructorArguments[0].Value?.ToString();
|
||||||
|
|
||||||
|
if (beefTypeName is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MappedType newMapping = DecodeType(beefTypeName, beefTypeToMappedType);
|
||||||
|
newMapping.CSharpTypeName = csharpTypeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder engineFunctions = new StringBuilder();
|
||||||
|
StringBuilder wrappers = new StringBuilder();
|
||||||
|
|
||||||
|
engineFunctions.Append("""
|
||||||
|
// <auto-generated />
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
|
unsafe internal partial struct EngineFunctions
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
""");
|
||||||
|
|
||||||
|
wrappers.Append("""
|
||||||
|
// <auto-generated />
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GlitchyEngine;
|
||||||
|
|
||||||
|
unsafe internal partial class ScriptGlue
|
||||||
|
{
|
||||||
|
""");
|
||||||
|
|
||||||
|
List<GlueMethod> methods = new List<GlueMethod>();
|
||||||
|
|
||||||
|
foreach (string glueFunctions in input.GlueFunctionDefinitions)
|
||||||
|
{
|
||||||
|
methods.AddRange(JsonSerializer.Deserialize<GlueMethod[]>(glueFunctions) ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (GlueMethod method in methods)
|
||||||
|
{
|
||||||
|
if (!input.ExistingEngineFunctionStructs.Contains(method.Name))
|
||||||
|
{
|
||||||
|
GenerateFunctionPointer(method, beefTypeToMappedType, engineFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input.ExistingScriptGlueClasses.Contains(method.Name))
|
||||||
|
{
|
||||||
|
GenerateWrapper(method, beefTypeToMappedType, wrappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// foreach (var type in input.enumerations)
|
||||||
|
// {
|
||||||
|
// if (type is null)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// GenerateCode(type, output);
|
||||||
|
// var typeNamespace = type.ContainingNamespace.IsGlobalNamespace
|
||||||
|
// ? null
|
||||||
|
// : $"{type.ContainingNamespace}.";
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
engineFunctions.Append("\n*/}");
|
||||||
|
wrappers.Append("\n}");
|
||||||
|
|
||||||
|
context.AddSource("EngineFunctions.g.cs", engineFunctions.ToString());
|
||||||
|
context.AddSource("ScriptGlue.g.cs", wrappers.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsScriptGlueClass(
|
||||||
|
SyntaxNode syntaxNode,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return syntaxNode is ClassDeclarationSyntax { Identifier.ValueText: "ScriptGlue" };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsEngineFunctionsStruct (
|
||||||
|
SyntaxNode syntaxNode,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return syntaxNode is StructDeclarationSyntax { Identifier.ValueText: "EngineFunctions" };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INamedTypeSymbol? GetSymbol<T>(GeneratorSyntaxContext context,
|
||||||
|
CancellationToken cancellationToken) where T : BaseTypeDeclarationSyntax
|
||||||
|
{
|
||||||
|
var classDecl = (T)context.Node;
|
||||||
|
return context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CouldBeMappedType(
|
||||||
|
SyntaxNode syntaxNode,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (syntaxNode is not AttributeSyntax attribute)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var name = ExtractName(attribute.Name);
|
||||||
|
|
||||||
|
return name is "EngineClass" or "EngineClassAttribute";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ExtractName(NameSyntax? name)
|
||||||
|
{
|
||||||
|
return name switch
|
||||||
|
{
|
||||||
|
SimpleNameSyntax ins => ins.Identifier.Text,
|
||||||
|
QualifiedNameSyntax qns => qns.Right.Identifier.Text,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ITypeSymbol? GetTypeOrNull(
|
||||||
|
GeneratorSyntaxContext context,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var attributeSyntax = (AttributeSyntax)context.Node;
|
||||||
|
|
||||||
|
// "attribute.Parent" is "AttributeListSyntax"
|
||||||
|
// "attribute.Parent.Parent" is a C# fragment the attributes are applied to
|
||||||
|
if (attributeSyntax.Parent?.Parent is not BaseTypeDeclarationSyntax typeDeclaration)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return context.SemanticModel.GetDeclaredSymbol(typeDeclaration) as ITypeSymbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user