Browse Source

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 years ago
parent
commit
0c0a1a6b91
3 changed files with 70 additions and 63 deletions
  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 -> ()
 	| _ -> spr ctx ";"
 
+
 let rec concat ctx s f = function
 	| [] -> ()
 	| [x] -> f x
@@ -276,17 +277,17 @@ let rec gen_call ctx e el in_value =
 		(match ctx.current.cl_super with
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| 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;
-			spr ctx ")})";
+			spr ctx ")";
 		);
 	| TField ({ eexpr = TConst TSuper },f) , params ->
 		(match ctx.current.cl_super with
 		| None -> error "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 			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;
 			spr ctx ")";
 		);
@@ -298,14 +299,13 @@ let rec gen_call ctx e el in_value =
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")";
 	| 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 ")";
 	| TLocal { v_name = "__new__" }, e :: params ->
 		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 ")";
 	| TLocal { v_name = "__callself__" }, { eexpr = TConst (TString head) } :: { eexpr = TConst (TString tail) } :: el ->
 		print ctx "%s:%s" head tail;
@@ -524,8 +524,8 @@ and gen_expr ctx e =
 				    gen_value ctx e;
 		end
 	| 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 ")"
 	| TIf (cond,e,eelse) ->
 		ctx.iife_assign <- true;
@@ -1002,7 +1002,7 @@ and gen_cond ctx cond =
 and has_class ctx c =
     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
 
 and can_gen_class_field ctx = function
@@ -1073,7 +1073,7 @@ let gen_class_field ctx c f =
 		    ctx.in_value <- None;
 		    ctx.in_loop <- false;
 		    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;
 		    let fblock = fun_block ctx f2 e.epos in
 		    (match fblock.eexpr with
@@ -1134,17 +1134,17 @@ let generate_class ctx c =
 				    let old = ctx.in_value, ctx.in_loop in
 				    ctx.in_value <- None;
 				    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
 				    (match fblock.eexpr with
 				    | TBlock el ->
 					let bend = open_block ctx in
 					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;
 					newline ctx;
-					(* TODO: use nonconflict var instead of mt *)
+					(* TODO: use nonconflict var instead of prototype *)
 					spr ctx "return self";
 					bend();
 					newline ctx;
@@ -1186,16 +1186,8 @@ let generate_class ctx c =
 	List.iter (gen_class_static_field ctx c) c.cl_ordered_statics;
 
 	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;
 
 		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 ->
 				newprop ctx;
 				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;
 				print ctx "__properties__ =  {%s}" (gen_props props));
 		end;
 
 		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
 
 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) {
-		if( cl == null )
-			return false;
+		if( cl == null ) return false;
+
 		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 {
 		tabobj.length = length;
 		setmetatable(tabobj, {
-			__index : __lua__("Array.mt"),
+			__index : __lua__("Array.prototype"),
 			__newindex : lua.Boot.arrayNewIndex
 		});
 		return tabobj;
@@ -138,6 +140,7 @@ class Boot {
 				case "userdata": return "<userdata>";
 				case "function": return "<function>";
 				case "thread": return "<thread>";
+				// TODO: come up with better fix for infinite recursive loop due to __class__
 				case "table": { __lua__("local result = '';
 		if o.toString ~= nil then result = o:toString()
 		elseif o.__tostring ~= nil then result = tostring(o)
@@ -146,10 +149,14 @@ class Boot {
 			result = result .. '{ ';
 			local first = true
 			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
-				result = result .. i .. ' => ' .. lua.Boot.__string_rec(v, s .. 'o');
 			end
 			result = result .. ' }';
 		end");

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

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