Explorar el Código

[lua] Share metatables with other class instances (#11103)

* [lua] enable metatable sharing within same class (opt-in)

* [lua] actually, make the shared metatable the default

* [lua] make lua.Lua.getmetatable accept Any as parameter

https://www.lua.org/manual/5.1/manual.html#pdf-getmetatable

* [tests] check if Lua target actually shares metatables within class

* [lua] revert lua.Lua change
Frixuu hace 1 año
padre
commit
4aa876b8ca

+ 11 - 3
src/generators/genlua.ml

@@ -1550,7 +1550,7 @@ let check_multireturn ctx c =
 
 let check_field_name c f =
     match f.cf_name with
-    | "prototype" | "__proto__" | "constructor" ->
+    | "prototype" | "__proto__" | "constructor" | "__mt__" ->
         raise_typing_error ("The field name '" ^ f.cf_name ^ "'  is not allowed in Lua") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos);
     | _ -> ()
 
@@ -1660,8 +1660,10 @@ let generate_class ctx c =
                     | TBlock el ->
                         let bend = open_block ctx in
                         newline ctx;
-                        if not (has_prototype ctx c) then println ctx "local self = _hx_new()" else
-                            println ctx "local self = _hx_new(%s.prototype)" p;
+                        if not (has_prototype ctx c) then
+                            println ctx "local self = _hx_new()"
+                        else
+                            println ctx "local self = _hx_nsh(%s.__mt__)" p;
                         println ctx "%s.super(%s)" p (String.concat "," ("self" :: (List.map lua_arg_name f.tf_args)));
                         if p = "String" then println ctx "self = string";
                         spr ctx "return self";
@@ -1734,6 +1736,9 @@ let generate_class ctx c =
              if has_property_reflection && Codegen.has_properties csup then
                  println ctx "setmetatable(%s.prototype.__properties__,{__index=%s.prototype.__properties__})" p psup;
         );
+
+        (* Create a metatable specific for this class *)
+        println ctx "%s.__mt__ = _hx_mmt(%s.prototype)" p p;
     end
 
 let generate_enum ctx e =
@@ -2023,6 +2028,9 @@ let generate com =
     (* base lua metatables for prototypes, inheritance, etc. *)
     print_file (Common.find_file com "lua/_lua/_hx_anon.lua");
 
+    (* Helpers for creating metatables from prototypes *)
+    print_file (Common.find_file com "lua/_lua/_hx_objects.lua");
+
     (* base runtime class stubs for haxe value types (Int, Float, etc) *)
     print_file (Common.find_file com "lua/_lua/_hx_classes.lua");
 

+ 0 - 4
std/lua/_lua/_hx_anon.lua

@@ -28,10 +28,6 @@ local function _hx_o(obj)
   return setmetatable(obj, _hx_obj_mt)
 end
 
-local function _hx_new(prototype)
-  return setmetatable({__fields__ = {}}, {__newindex=_hx_obj_newindex, __index=prototype, __tostring=_hx_tostring})
-end
-
 function _hx_field_arr(obj)
     local res = {}
     local idx = 0

+ 15 - 0
std/lua/_lua/_hx_objects.lua

@@ -0,0 +1,15 @@
+local function _hx_mmt(prototype)
+  return {
+    __newindex = _hx_obj_newindex,
+    __index = prototype,
+    __tostring = _hx_tostring
+  }
+end
+
+local function _hx_new(prototype)
+  return setmetatable({ __fields__ = {} }, _hx_mmt(prototype))
+end
+
+local function _hx_nsh(metatable)
+  return setmetatable({ __fields__ = {} }, metatable)
+end

+ 1 - 1
std/lua/_lua/_hx_tab_array.lua

@@ -1,4 +1,4 @@
-local _hx_hidden = {__id__=true, hx__closures=true, super=true, prototype=true, __fields__=true, __ifields__=true, __class__=true, __properties__=true, __fields__=true, __name__=true}
+local _hx_hidden = {__id__=true, hx__closures=true, super=true, prototype=true, __fields__=true, __ifields__=true, __class__=true, __properties__=true, __mt__=true, __name__=true}
 
 _hx_array_mt = {
     __newindex = function(t,k,v)

+ 27 - 0
tests/unit/src/unit/TestLua.hx

@@ -1,5 +1,7 @@
 package unit;
 
+import utest.Assert;
+
 class TestLua extends Test {
 	function testMultiReturnWrap(){
 		var multi : Multi = untyped MultiCall.doit();
@@ -45,6 +47,27 @@ class TestLua extends Test {
 		untyped _hx_box_mr = old_hx_box_mr;
 	}
 
+	function testMetatablesAreShared() {
+
+		// New class instances get metatables assigned to them
+		final a = new TLA();
+		t(lua.Lua.getmetatable(cast a) != null);
+
+		// Instances of the same class share a metatable
+		final a2 = new TLA();
+		eq(lua.Lua.getmetatable(cast a), lua.Lua.getmetatable(cast a2));
+
+		// Subclass does not share a metatable with the parent
+		final aChild = new TLAChild();
+		t(lua.Lua.getmetatable(cast aChild) != null);
+		Assert.notEquals(lua.Lua.getmetatable(cast a), lua.Lua.getmetatable(cast aChild));
+
+		// Neither do any other arbitrary two classes
+		final b = new TLB();
+		t(lua.Lua.getmetatable(cast b) != null);
+		Assert.notEquals(lua.Lua.getmetatable(cast a), lua.Lua.getmetatable(cast b));
+		Assert.notEquals(lua.Lua.getmetatable(cast aChild), lua.Lua.getmetatable(cast b));
+	}
 }
 
 @:multiReturn extern class Multi {
@@ -60,3 +83,7 @@ class MultiCall {
 		return lua.Lua.type(m) == "table";
 	}
 }
+
+class TLA { private var foo: String; public function new() { this.foo = "A"; } }
+class TLAChild extends TLA { public function new() { super(); this.foo = "AChild"; } }
+class TLB { private var foo: String; public function new() { this.foo = "B"; } }