Jelajahi Sumber

Lua : refactor OOP

Previously I was using a hacked together oop implementation for lua I
came up with.   It worked ok for methods, but didn't quite get the
fields right in certain cases, specifically with certain usages of
super().

So, I've gone back to the drawing board with something more robust. The
biggest change is that in the generated Lua code, all constructors now
require at least a single argument, typically an anonymous table.

```lua
Foo.new({});
```
Haxe lua users won't have to worry about this, the constructor is
generated automatically with the base instance specified.

The main benefit of this is that rather than constructing a new instance
internally, this anonymous table serves as the base object from which to
extend the fields and methods.  This allows a very straightforward
implementation for super(), which can be a simple alias for the extended
class constructor, called on the current instance.  It will emulate the Haxe
specification for super perfectly.  This will also be nice for
extending base lua objects with haxe-specific functionality (such as
arrays).

I was hoping to put this off a bit longer, but it's tripping up some of
the tests.
Justin Donaldson 10 tahun lalu
induk
melakukan
0c0a1a6b91
3 mengubah file dengan 70 tambahan dan 63 penghapusan
  1. 29 29
      genlua.ml
  2. 40 33
      std/lua/Boot.hx
  3. 1 1
      std/lua/_std/String.hx

+ 29 - 29
genlua.ml

@@ -137,6 +137,7 @@ let semicolon ctx =
 	| '}' when not ctx.separator -> ()
 	| '}' when not ctx.separator -> ()
 	| _ -> spr ctx ";"
 	| _ -> spr ctx ";"
 
 
+
 let rec concat ctx s f = function
 let rec concat ctx s f = function
 	| [] -> ()
 	| [] -> ()
 	| [x] -> f x
 	| [x] -> f x
@@ -276,17 +277,17 @@ let rec gen_call ctx e el in_value =
 		(match ctx.current.cl_super with
 		(match ctx.current.cl_super with
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 		| Some (c,_) ->
-			print ctx "setmetatable(self, {__index = %s.new(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
+			print ctx "%s.new(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
-			spr ctx ")})";
+			spr ctx ")";
 		);
 		);
 	| TField ({ eexpr = TConst TSuper },f) , params ->
 	| TField ({ eexpr = TConst TSuper },f) , params ->
 		(match ctx.current.cl_super with
 		(match ctx.current.cl_super with
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 		| Some (c,_) ->
 			let name = field_name f in
 			let name = field_name f in
-			(* TODO: use nonconflict var instead of mt *)
-			print ctx "%s.mt%s(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
+			(* TODO: use nonconflict var instead of prototype *)
+			print ctx "%s.prototype%s(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 			spr ctx ")";
 		);
 		);
@@ -298,14 +299,13 @@ let rec gen_call ctx e el in_value =
 		concat ctx "," (gen_value ctx) el;
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")";
 		spr ctx ")";
 	| TLocal { v_name = "__new__" }, { eexpr = TConst (TString cl) } :: params ->
 	| TLocal { v_name = "__new__" }, { eexpr = TConst (TString cl) } :: params ->
-		print ctx "%s.new(" cl;
-		concat ctx "," (gen_value ctx) params;
+		print ctx "%s.new({}" cl;
+		List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 		spr ctx ")";
 		spr ctx ")";
 	| TLocal { v_name = "__new__" }, e :: params ->
 	| TLocal { v_name = "__new__" }, e :: params ->
 		gen_value ctx e;
 		gen_value ctx e;
-		spr ctx ".new";
-		spr ctx "(";
-		concat ctx "," (gen_value ctx) params;
+		spr ctx ".new({}";
+		List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 		spr ctx ")";
 		spr ctx ")";
 	| TLocal { v_name = "__callself__" }, { eexpr = TConst (TString head) } :: { eexpr = TConst (TString tail) } :: el ->
 	| TLocal { v_name = "__callself__" }, { eexpr = TConst (TString head) } :: { eexpr = TConst (TString tail) } :: el ->
 		print ctx "%s:%s" head tail;
 		print ctx "%s:%s" head tail;
@@ -524,8 +524,8 @@ and gen_expr ctx e =
 				    gen_value ctx e;
 				    gen_value ctx e;
 		end
 		end
 	| TNew (c,_,el) ->
 	| TNew (c,_,el) ->
-		print ctx "%s.new(" (ctx.type_accessor (TClassDecl c));
-		concat ctx "," (gen_value ctx) el;
+		print ctx "%s.new({}" (ctx.type_accessor (TClassDecl c));
+			List.iter (fun p -> print ctx ","; gen_value ctx p) el;
 		spr ctx ")"
 		spr ctx ")"
 	| TIf (cond,e,eelse) ->
 	| TIf (cond,e,eelse) ->
 		ctx.iife_assign <- true;
 		ctx.iife_assign <- true;
@@ -1002,7 +1002,7 @@ and gen_cond ctx cond =
 and has_class ctx c =
 and has_class ctx c =
     has_feature ctx "lua.Boot.getClass" && (c.cl_super <> None || c.cl_ordered_fields <> [] || c.cl_constructor <> None)
     has_feature ctx "lua.Boot.getClass" && (c.cl_super <> None || c.cl_ordered_fields <> [] || c.cl_constructor <> None)
 
 
-and has_metatable ctx c =
+and has_prototype ctx c =
     c.cl_super <> None || (has_class ctx c) || List.exists (can_gen_class_field ctx) c.cl_ordered_fields
     c.cl_super <> None || (has_class ctx c) || List.exists (can_gen_class_field ctx) c.cl_ordered_fields
 
 
 and can_gen_class_field ctx = function
 and can_gen_class_field ctx = function
@@ -1073,7 +1073,7 @@ let gen_class_field ctx c f =
 		    ctx.in_value <- None;
 		    ctx.in_value <- None;
 		    ctx.in_loop <- false;
 		    ctx.in_loop <- false;
 		    print ctx "%s = function" (anon_field f.cf_name);
 		    print ctx "%s = function" (anon_field f.cf_name);
-		    print ctx "(%s) " (String.concat "," ("self"::(List.map ident (List.map arg_name f2.tf_args))));
+		    print ctx "(%s) " (String.concat "," (List.map ident (List.map arg_name f2.tf_args)));
 		    newline ctx;
 		    newline ctx;
 		    let fblock = fun_block ctx f2 e.epos in
 		    let fblock = fun_block ctx f2 e.epos in
 		    (match fblock.eexpr with
 		    (match fblock.eexpr with
@@ -1134,17 +1134,17 @@ let generate_class ctx c =
 				    let old = ctx.in_value, ctx.in_loop in
 				    let old = ctx.in_value, ctx.in_loop in
 				    ctx.in_value <- None;
 				    ctx.in_value <- None;
 				    ctx.in_loop <- false;
 				    ctx.in_loop <- false;
-				    print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+				    print ctx "function(%s) " (String.concat "," ("self" :: List.map ident (List.map arg_name f.tf_args)));
 				    let fblock = fun_block ctx f e.epos in
 				    let fblock = fun_block ctx f e.epos in
 				    (match fblock.eexpr with
 				    (match fblock.eexpr with
 				    | TBlock el ->
 				    | TBlock el ->
 					let bend = open_block ctx in
 					let bend = open_block ctx in
 					newline ctx;
 					newline ctx;
-					if (has_metatable ctx c) then
-					    print ctx "local self = %s.mt" p;
+					if (has_prototype ctx c) then
+					    print ctx "setmetatable(self, {__index=%s.prototype}) " p;
 					List.iter (gen_block_element ctx) el;
 					List.iter (gen_block_element ctx) el;
 					newline ctx;
 					newline ctx;
-					(* TODO: use nonconflict var instead of mt *)
+					(* TODO: use nonconflict var instead of prototype *)
 					spr ctx "return self";
 					spr ctx "return self";
 					bend();
 					bend();
 					newline ctx;
 					newline ctx;
@@ -1186,16 +1186,8 @@ let generate_class ctx c =
 	List.iter (gen_class_static_field ctx c) c.cl_ordered_statics;
 	List.iter (gen_class_static_field ctx c) c.cl_ordered_statics;
 
 
 	newline ctx;
 	newline ctx;
-	if (has_metatable ctx c) then begin
-		(match c.cl_super with
-		| None -> ()
-		| Some (csup,_) ->
-			let psup = ctx.type_accessor (TClassDecl csup) in
-			print ctx "%s.__super__ = %s" p psup;
-			newline ctx;
-			(* TODO: use nonconflict var instead of mt *)
-		);
-		print ctx "%s.mt = {" p;
+	if (has_prototype ctx c) then begin
+		print ctx "%s.prototype = {" p;
 		newline ctx;
 		newline ctx;
 
 
 		List.iter (fun f -> if can_gen_class_field ctx f then gen_class_field ctx c f) c.cl_ordered_fields;
 		List.iter (fun f -> if can_gen_class_field ctx f then gen_class_field ctx c f) c.cl_ordered_fields;
@@ -1211,14 +1203,22 @@ let generate_class ctx c =
 			| Some (csup,_) when Codegen.has_properties csup ->
 			| Some (csup,_) when Codegen.has_properties csup ->
 				newprop ctx;
 				newprop ctx;
 				let psup = s_path ctx csup.cl_path in
 				let psup = s_path ctx csup.cl_path in
-				print ctx "__properties__ =  {__index = %s.mt.__properties__,{%s}}" psup (gen_props props)
+				print ctx "__properties__ =  {__index = %s.prototype.__properties__,{%s}}" psup (gen_props props)
 			| _ ->
 			| _ ->
 				newprop ctx;
 				newprop ctx;
 				print ctx "__properties__ =  {%s}" (gen_props props));
 				print ctx "__properties__ =  {%s}" (gen_props props));
 		end;
 		end;
 
 
 		print ctx "\n}";
 		print ctx "\n}";
-		newline ctx
+		newline ctx;
+		(match c.cl_super with
+		| None -> ()
+		| Some (csup,_) ->
+			let psup = ctx.type_accessor (TClassDecl csup) in
+			print ctx "%s.__super__ = %s" p psup; newline ctx;
+			print ctx "setmetatable(%s.prototype,{__index=%s.prototype})" p psup; newline ctx;
+			(* TODO: use nonconflict var instead of prototype *)
+		);
 	end
 	end
 
 
 let generate_enum ctx e =
 let generate_enum ctx e =

+ 40 - 33
std/lua/Boot.hx

@@ -46,37 +46,39 @@ class Boot {
 	}
 	}
 
 
 	@:ifFeature("typed_catch") private static function __instanceof(o : Dynamic,cl : Dynamic) {
 	@:ifFeature("typed_catch") private static function __instanceof(o : Dynamic,cl : Dynamic) {
-		if( cl == null )
-			return false;
+		if( cl == null ) return false;
+
 		switch( cl ) {
 		switch( cl ) {
-		case Int:
-			return (untyped __lua__("bitor(o,0) == o"));
-		case Float:
-			return untyped __type__(o) == "number";
-		case Bool:
-			return untyped __type__(o) == "boolean";
-		case String:
-			return untyped __type__(o) == "string";
-		case Array:
-			// TODO: Better array check
-			return untyped __type__(o) == "table" && o.__enum__ == null;
-		case Dynamic:
-			return true;
-		default:
-			if( o != null ) {
-				// Check if o is an instance of a Haxe class or a native JS object
-				if (untyped __type__(cl) == "table" ) {
-					// TODO: Fixme
-					return true;
+			case Int:
+				return (untyped __lua__("bitor(o,0) == o"));
+			case Float:
+				return untyped __type__(o) == "number";
+			case Bool:
+				return untyped __type__(o) == "boolean";
+			case String:
+				return untyped __type__(o) == "string";
+			case Array:
+				// TODO: Better array check
+				return untyped __type__(o) == "table" 
+					&& o.__enum__ == null
+					&& o.length != null;
+			case Dynamic:
+				return true;
+			default:
+				if( o != null ) {
+					// Check if o is an instance of a Haxe class or a native JS object
+					if (untyped __type__(cl) == "table" ) {
+						// TODO: Fixme
+						return true;
+					}
+				} else {
+					return false;
 				}
 				}
-			} else {
-				return false;
-			}
-
-			// do not use isClass/isEnum here
-			untyped __feature__("Class.*",if( cl == Class && o.__name__ != null ) return true);
-			untyped __feature__("Enum.*",if( cl == Enum && o.__ename__ != null ) return true);
-			return o.__enum__ == cl;
+
+				// do not use isClass/isEnum here
+				untyped __feature__("Class.*",if( cl == Class && o.__name__ != null ) return true);
+				untyped __feature__("Enum.*",if( cl == Enum && o.__ename__ != null ) return true);
+				return o.__enum__ == cl;
 		}
 		}
 	}
 	}
 
 
@@ -97,7 +99,7 @@ class Boot {
 	public static function defArray(tabobj: Dynamic, length : Int) : Array<Dynamic>  untyped {
 	public static function defArray(tabobj: Dynamic, length : Int) : Array<Dynamic>  untyped {
 		tabobj.length = length;
 		tabobj.length = length;
 		setmetatable(tabobj, {
 		setmetatable(tabobj, {
-			__index : __lua__("Array.mt"),
+			__index : __lua__("Array.prototype"),
 			__newindex : lua.Boot.arrayNewIndex
 			__newindex : lua.Boot.arrayNewIndex
 		});
 		});
 		return tabobj;
 		return tabobj;
@@ -138,6 +140,7 @@ class Boot {
 				case "userdata": return "<userdata>";
 				case "userdata": return "<userdata>";
 				case "function": return "<function>";
 				case "function": return "<function>";
 				case "thread": return "<thread>";
 				case "thread": return "<thread>";
+				// TODO: come up with better fix for infinite recursive loop due to __class__
 				case "table": { __lua__("local result = '';
 				case "table": { __lua__("local result = '';
 		if o.toString ~= nil then result = o:toString()
 		if o.toString ~= nil then result = o:toString()
 		elseif o.__tostring ~= nil then result = tostring(o)
 		elseif o.__tostring ~= nil then result = tostring(o)
@@ -146,10 +149,14 @@ class Boot {
 			result = result .. '{ ';
 			result = result .. '{ ';
 			local first = true
 			local first = true
 			for i, v in pairs(o) do
 			for i, v in pairs(o) do
-				if (first) then first = false
-				else result = result .. ','
+				if i ~= '__class__' then
+					if (first) then 
+						first = false
+					else 
+						result = result .. ','
+					end
+					result = result .. i .. ' => ' .. lua.Boot.__string_rec(v, s .. 'o');
 				end
 				end
-				result = result .. i .. ' => ' .. lua.Boot.__string_rec(v, s .. 'o');
 			end
 			end
 			result = result .. ' }';
 			result = result .. ' }';
 		end");
 		end");

+ 1 - 1
std/lua/_std/String.hx

@@ -26,7 +26,7 @@ class String {
 	public function new(string:String) untyped {}
 	public function new(string:String) untyped {}
 
 
 	static function __init__() : Void{
 	static function __init__() : Void{
-		untyped __lua__("setmetatable(_G.string, String.mt)");
+		untyped __lua__("setmetatable(_G.string, String.prototype)");
 	}
 	}
 
 
 	@:keep
 	@:keep