|
|
@@ -0,0 +1,214 @@
|
|
|
+using Microsoft.CodeAnalysis;
|
|
|
+
|
|
|
+namespace Lua.SourceGenerator;
|
|
|
+
|
|
|
+partial class LuaObjectGenerator
|
|
|
+{
|
|
|
+ static bool TryEmit(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var error = false;
|
|
|
+
|
|
|
+ // must be partial
|
|
|
+ if (!typeMetadata.IsPartial())
|
|
|
+ {
|
|
|
+ context.ReportDiagnostic(Diagnostic.Create(
|
|
|
+ DiagnosticDescriptors.MustBePartial,
|
|
|
+ typeMetadata.Syntax.Identifier.GetLocation(),
|
|
|
+ typeMetadata.Symbol.Name));
|
|
|
+ error = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // nested is not allowed
|
|
|
+ if (typeMetadata.IsNested())
|
|
|
+ {
|
|
|
+ context.ReportDiagnostic(Diagnostic.Create(
|
|
|
+ DiagnosticDescriptors.NestedNotAllowed,
|
|
|
+ typeMetadata.Syntax.Identifier.GetLocation(),
|
|
|
+ typeMetadata.Symbol.Name));
|
|
|
+ error = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // verify abstract/interface
|
|
|
+ if (typeMetadata.Symbol.IsAbstract)
|
|
|
+ {
|
|
|
+ context.ReportDiagnostic(Diagnostic.Create(
|
|
|
+ DiagnosticDescriptors.AbstractNotAllowed,
|
|
|
+ typeMetadata.Syntax.Identifier.GetLocation(),
|
|
|
+ typeMetadata.TypeName));
|
|
|
+ error = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (error)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine("// <auto-generated />");
|
|
|
+ builder.AppendLine("#nullable enable");
|
|
|
+ builder.AppendLine("#pragma warning disable CS0162 // Unreachable code");
|
|
|
+ builder.AppendLine("#pragma warning disable CS0219 // Variable assigned but never used");
|
|
|
+ builder.AppendLine("#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.");
|
|
|
+ builder.AppendLine("#pragma warning disable CS8601 // Possible null reference assignment");
|
|
|
+ builder.AppendLine("#pragma warning disable CS8602 // Possible null return");
|
|
|
+ builder.AppendLine("#pragma warning disable CS8604 // Possible null reference argument for parameter");
|
|
|
+ builder.AppendLine("#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method");
|
|
|
+ builder.AppendLine();
|
|
|
+
|
|
|
+ var ns = typeMetadata.Symbol.ContainingNamespace;
|
|
|
+ if (!ns.IsGlobalNamespace)
|
|
|
+ {
|
|
|
+ builder.AppendLine($"namespace {ns}");
|
|
|
+ builder.BeginBlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ var typeDeclarationKeyword = (typeMetadata.Symbol.IsRecord, typeMetadata.Symbol.IsValueType) switch
|
|
|
+ {
|
|
|
+ (true, true) => "record struct",
|
|
|
+ (true, false) => "record",
|
|
|
+ (false, true) => "struct",
|
|
|
+ (false, false) => "class",
|
|
|
+ };
|
|
|
+
|
|
|
+ using var _ = builder.BeginBlockScope($"partial {typeDeclarationKeyword} {typeMetadata.TypeName} : global::Lua.ILuaUserData");
|
|
|
+
|
|
|
+ // add ILuaUserData impl
|
|
|
+ builder.Append(
|
|
|
+"""
|
|
|
+ global::Lua.LuaTable? global::Lua.ILuaUserData.Metatable
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (__metatable != null) return __metatable;
|
|
|
+
|
|
|
+ __metatable = new();
|
|
|
+ __metatable[global::Lua.Runtime.Metamethods.Index] = __metamethod_index;
|
|
|
+ __metatable[global::Lua.Runtime.Metamethods.NewIndex] = __metamethod_newindex;
|
|
|
+ return __metatable;
|
|
|
+ }
|
|
|
+ set
|
|
|
+ {
|
|
|
+ __metatable = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ static global::Lua.LuaTable? __metatable;
|
|
|
+
|
|
|
+""", false);
|
|
|
+
|
|
|
+ if (!TryEmitIndexMetamethod(typeMetadata, builder, context))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!TryEmitNewIndexMetamethod(typeMetadata, builder, context))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ns.IsGlobalNamespace) builder.EndBlock();
|
|
|
+
|
|
|
+ builder.AppendLine("#pragma warning restore CS0162 // Unreachable code");
|
|
|
+ builder.AppendLine("#pragma warning restore CS0219 // Variable assigned but never used");
|
|
|
+ builder.AppendLine("#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.");
|
|
|
+ builder.AppendLine("#pragma warning restore CS8601 // Possible null reference assignment");
|
|
|
+ builder.AppendLine("#pragma warning restore CS8602 // Possible null return");
|
|
|
+ builder.AppendLine("#pragma warning restore CS8604 // Possible null reference argument for parameter");
|
|
|
+ builder.AppendLine("#pragma warning restore CS8631 // The type cannot be used as type parameter in the generic type or method");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ catch (Exception)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context)
|
|
|
+ {
|
|
|
+ builder.AppendLine("static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction((context, buffer, ct) =>");
|
|
|
+
|
|
|
+ using (builder.BeginBlockScope())
|
|
|
+ {
|
|
|
+ builder.AppendLine($"var userData = context.GetArgument<{typeMetadata.FullTypeName}>(0);");
|
|
|
+ builder.AppendLine($"var key = context.GetArgument<global::System.String>(1);");
|
|
|
+ builder.AppendLine("var result = key switch");
|
|
|
+
|
|
|
+ using (builder.BeginBlockScope())
|
|
|
+ {
|
|
|
+ foreach (var propertyMetadata in typeMetadata.Properties)
|
|
|
+ {
|
|
|
+ if (propertyMetadata.IsStatic)
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"""{propertyMetadata.LuaMemberName}"" => new global::Lua.LuaValue({typeMetadata.FullTypeName}.{propertyMetadata.Symbol.Name}),");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"""{propertyMetadata.LuaMemberName}"" => new global::Lua.LuaValue(userData.{propertyMetadata.Symbol.Name}),");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine(@$"_ => throw new global::Lua.LuaRuntimeException(context.State.GetTraceback(), $""{typeMetadata.LuaTypeName}.{{key}} not found.""),");
|
|
|
+ }
|
|
|
+ builder.AppendLine(";");
|
|
|
+
|
|
|
+ builder.AppendLine("buffer.Span[0] = result;");
|
|
|
+ builder.AppendLine("return new(1);");
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine(");");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context)
|
|
|
+ {
|
|
|
+ builder.AppendLine("static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction((context, buffer, ct) =>");
|
|
|
+
|
|
|
+ using (builder.BeginBlockScope())
|
|
|
+ {
|
|
|
+ builder.AppendLine($"var userData = context.GetArgument<{typeMetadata.FullTypeName}>(0);");
|
|
|
+ builder.AppendLine($"var key = context.GetArgument<global::System.String>(1);");
|
|
|
+ builder.AppendLine("switch (key)");
|
|
|
+
|
|
|
+ using (builder.BeginBlockScope())
|
|
|
+ {
|
|
|
+ foreach (var propertyMetadata in typeMetadata.Properties)
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"case ""{propertyMetadata.LuaMemberName}"":");
|
|
|
+
|
|
|
+ using (builder.BeginIndentScope())
|
|
|
+ {
|
|
|
+ if (propertyMetadata.IsReadOnly)
|
|
|
+ {
|
|
|
+ builder.AppendLine($@"throw new global::Lua.LuaRuntimeException(context.State.GetTraceback(), $""{typeMetadata.LuaTypeName}.{{key}}cannot overwrite."");");
|
|
|
+ }
|
|
|
+ else if (propertyMetadata.IsStatic)
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"{typeMetadata.FullTypeName}.{propertyMetadata.Symbol.Name} = context.GetArgument<{propertyMetadata.TypeFullName}>(2);");
|
|
|
+ builder.AppendLine("break;");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"userData.{propertyMetadata.Symbol.Name} = context.GetArgument<{propertyMetadata.TypeFullName}>(2);");
|
|
|
+ builder.AppendLine("break;");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine(@$"default:");
|
|
|
+
|
|
|
+ using (builder.BeginIndentScope())
|
|
|
+ {
|
|
|
+ builder.AppendLine(@$"throw new global::Lua.LuaRuntimeException(context.State.GetTraceback(), $""{typeMetadata.LuaTypeName}.{{key}} not found."");");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine("return new(0);");
|
|
|
+ }
|
|
|
+
|
|
|
+ builder.AppendLine(");");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|