Sfoglia il codice sorgente

add: support __index and __newindex Metamethod in SourceGenerator

akeit0 2 settimane fa
parent
commit
4631ceb5c9

+ 23 - 11
src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs

@@ -100,14 +100,24 @@ partial class LuaObjectGenerator
                 return false;
             }
 
-            if (!TryEmitIndexMetamethod(typeMetadata, builder, references, compilation, context, tempCollections))
+            if(!metamethodSet.Contains(LuaObjectMetamethod.Index))
             {
-                return false;
-            }
+                if (!TryEmitIndexMetamethod(typeMetadata, builder, references, compilation, context, tempCollections))
+                {
+                    return false;
+                }
 
-            if (!TryEmitNewIndexMetamethod(typeMetadata, builder, references, context, tempCollections))
+                metamethodSet.Add(LuaObjectMetamethod.Index);
+            }
+           
+            if(!metamethodSet.Contains(LuaObjectMetamethod.NewIndex))
             {
-                return false;
+                if (!TryEmitNewIndexMetamethod(typeMetadata, builder, references, context, tempCollections))
+                {
+                    return false;
+                }
+
+                metamethodSet.Add(LuaObjectMetamethod.NewIndex);
             }
 
             if (!TryEmitMetatable(builder, metamethodSet, context))
@@ -472,13 +482,14 @@ partial class LuaObjectGenerator
 
             if (methodMetadata.HasMetamethodAttribute)
             {
+                var metaMethodName = methodMetadata.Metamethod.ToString().ToLower();
                 if (!metamethodSet.Add(methodMetadata.Metamethod))
                 {
                     context.ReportDiagnostic(Diagnostic.Create(
                         DiagnosticDescriptors.DuplicateMetamethod,
                         methodMetadata.Symbol.Locations.FirstOrDefault(),
                         typeMetadata.TypeName,
-                        methodMetadata.Metamethod
+                        metaMethodName
                     ));
 
                     continue;
@@ -486,11 +497,11 @@ partial class LuaObjectGenerator
 
                 if (functionName == null)
                 {
-                    EmitMethodFunction($"__metamethod_{methodMetadata.Metamethod}", methodMetadata.Metamethod.ToString().ToLower(), typeMetadata, methodMetadata, builder, references, compilation);
+                    EmitMethodFunction($"__metamethod_{metaMethodName}", metaMethodName, typeMetadata, methodMetadata, builder, references, compilation);
                 }
                 else
                 {
-                    builder.AppendLine($"static global::Lua.LuaFunction __metamethod_{methodMetadata.Metamethod} => {functionName};");
+                    builder.AppendLine($"static global::Lua.LuaFunction __metamethod_{metaMethodName} => {functionName};");
                 }
             }
         }
@@ -624,14 +635,15 @@ partial class LuaObjectGenerator
             builder.AppendLine("get");
             using (builder.BeginBlockScope())
             {
+                
                 builder.AppendLine("if (__metatable != null) return __metatable;");
                 builder.AppendLine();
                 builder.AppendLine("__metatable = new();");
-                builder.AppendLine("__metatable[global::Lua.Runtime.Metamethods.Index] = __metamethod_index;");
-                builder.AppendLine("__metatable[global::Lua.Runtime.Metamethods.NewIndex] = __metamethod_newindex;");
                 foreach (var metamethod in metamethods)
                 {
-                    builder.AppendLine($"__metatable[global::Lua.Runtime.Metamethods.{metamethod}] = __metamethod_{metamethod};");
+                    var metaMethodName = metamethod.ToString();
+                    var lowerName = metaMethodName.ToLower();
+                    builder.AppendLine($"__metatable[global::Lua.Runtime.Metamethods.{metaMethodName}] = __metamethod_{lowerName};");
                 }
 
                 builder.AppendLine("return __metatable;");

+ 3 - 1
src/Lua.SourceGenerator/LuaObjectMetamethod.cs

@@ -19,5 +19,7 @@ enum LuaObjectMetamethod
     Concat,
     Pairs,
     IPairs,
-    ToString
+    ToString,
+    Index,
+    NewIndex,
 }

+ 3 - 1
src/Lua/LuaObjectMetamethod.cs

@@ -17,5 +17,7 @@ public enum LuaObjectMetamethod
     Concat,
     Pairs,
     IPairs,
-    ToString
+    ToString,
+    Index,
+    NewIndex
 }

+ 54 - 0
tests/Lua.Tests/LuaObjectTests.cs

@@ -97,6 +97,30 @@ public partial class TestUserData
     }
 }
 
+[LuaObject]
+public partial class IntArrayUserData
+{
+    public int[] Array { get; } = new int[10];
+
+    public int this[int index]
+    {
+        get => Array[index];
+        set => Array[index] = value;
+    }
+
+    [LuaMetamethod(LuaObjectMetamethod.Index)]
+    public int GetAt(int index)
+    {
+        return Array[index];
+    }
+
+    [LuaMetamethod(LuaObjectMetamethod.NewIndex)]
+    public void SetAt(int index, int value)
+    {
+        Array[index] = value;
+    }
+}
+
 public class LuaObjectTests
 {
     [Test]
@@ -228,4 +252,34 @@ public class LuaObjectTests
         Assert.That(objSub.X, Is.EqualTo(-2));
         Assert.That(objSub.Y, Is.EqualTo(-2));
     }
+
+    [Test]
+    public async Task Test_IndexMetamethod()
+    {
+        var userData = new IntArrayUserData();
+
+        userData[0] = 1;
+        userData[1] = 1;
+        userData[2] = 2;
+        userData[3] = 3;
+        userData[4] = 5;
+
+        var state = LuaState.Create();
+        state.OpenBasicLibrary();
+        state.Environment["TestObj"] = userData;
+        var results = await state.DoStringAsync("""
+                                                return TestObj[0], TestObj[1], TestObj[2], TestObj[3], TestObj[4]
+                                                """);
+        Assert.That(results, Has.Length.EqualTo(5));
+        Assert.That(results[0].TryRead<int>(out var result), Is.True);
+        Assert.That(result, Is.EqualTo(1));
+        Assert.That(results[1].TryRead<int>(out result), Is.True);
+        Assert.That(result, Is.EqualTo(1));
+        Assert.That(results[2].TryRead<int>(out result), Is.True);
+        Assert.That(result, Is.EqualTo(2));
+        Assert.That(results[3].TryRead<int>(out result), Is.True);
+        Assert.That(result, Is.EqualTo(3));
+        Assert.That(results[4].TryRead<int>(out result), Is.True);
+        Assert.That(result, Is.EqualTo(5));
+    }
 }