Browse Source

Merge branch 'development' into genpy

Simon Krajewski 11 years ago
parent
commit
f3287ddd9e

+ 1 - 1
README.md

@@ -31,7 +31,7 @@ The Haxe project has several licenses, covering different parts of the projects.
  * The Haxe libraries are released under a "two-clause" BSD license.
  * The Neko runtime is licensed under the GNU Lesser General Public License version 2.1 or any later version.
 
-For the complete Haxe licenses, please see http://haxe.org/doc/license or [doc/LICENSE.txt](doc/LICENSE.txt).
+For the complete Haxe licenses, please see http://haxe.org/doc/license or [extra/LICENSE.txt](extra/LICENSE.txt).
 
 ## Installing Haxe
 

+ 95 - 0
codegen.ml

@@ -763,6 +763,18 @@ module Abstract = struct
 				let cf,m = find_multitype_specialization a pl e.epos in
 				let e = make_static_call ctx c cf a pl ((mk (TConst TNull) (TAbstract(a,pl)) e.epos) :: el) m e.epos in
 				{e with etype = m}
+			| TCall({eexpr = TField(_,FStatic({cl_path=[],"Std"},{cf_name = "string"}))},[e1]) when (match follow e1.etype with TAbstract({a_impl = Some _},_) -> true | _ -> false) ->
+				begin match follow e1.etype with
+					| TAbstract({a_impl = Some c} as a,_) ->
+						begin try
+							let cf = PMap.find "toString" c.cl_statics in
+							make_static_call ctx c cf a [] [e1] ctx.t.tstring e.epos
+						with Not_found ->
+							e
+						end
+					| _ ->
+						assert false
+				end
 			| TCall(e1, el) ->
 				begin try
 					begin match e1.eexpr with
@@ -1519,3 +1531,86 @@ struct
 
 			List.map fst (loop [] !rated)
 end;;
+
+
+module UnificationCallback = struct
+	let tf_stack = ref []
+
+	let check_call_params f el tl =
+		let rec loop acc el tl = match el,tl with
+			| e :: el, (n,_,t) :: tl ->
+				loop ((f e t) :: acc) el tl
+			| [], [] ->
+				acc
+			| [],_ ->
+				acc
+			| e :: el, [] ->
+				loop (e :: acc) el []
+		in
+		List.rev (loop [] el tl)
+
+	let check_call f el t = match follow t with
+		| TFun(args,_) ->
+			check_call_params f el args
+		| _ ->
+			el
+
+	let rec run f e =
+		let f e t =
+			if not (type_iseq e.etype t) then f e t else e
+		in
+		let check e = match e.eexpr with
+			| TBinop((OpAssign | OpAssignOp _ as op),e1,e2) ->
+				let e2 = f e2 e1.etype in
+				{e with eexpr = TBinop(op,e1,e2)}
+			| TVar(v,Some e) ->
+				let eo = Some (f e v.v_type) in
+				{ e with eexpr = TVar(v,eo) }
+			| TCall(e1,el) ->
+				let el = check_call f el e1.etype in
+				{e with eexpr = TCall(e1,el)}
+			| TNew(c,tl,el) ->
+				begin try
+					let tcf,_ = get_constructor (fun cf -> apply_params c.cl_types tl cf.cf_type) c in
+					let el = check_call f el tcf in
+					{e with eexpr = TNew(c,tl,el)}
+				with Not_found ->
+					e
+				end
+			| TArrayDecl el ->
+				begin match follow e.etype with
+					| TInst({cl_path=[],"Array"},[t]) -> {e with eexpr = TArrayDecl(List.map (fun e -> f e t) el)}
+					| _ -> e
+				end
+			| TObjectDecl fl ->
+				begin match follow e.etype with
+					| TAnon an ->
+						let fl = List.map (fun (n,e) ->
+							let e = try
+								let t = (PMap.find n an.a_fields).cf_type in
+								f e t
+							with Not_found ->
+								e
+							in
+							n,e
+						) fl in
+						{ e with eexpr = TObjectDecl fl }
+					| _ -> e
+				end
+			| TReturn (Some e1) ->
+				begin match !tf_stack with
+					| tf :: _ -> { e with eexpr = TReturn (Some (f e1 tf.tf_type))}
+					| _ -> e
+				end
+			| _ ->
+				e
+		in
+		match e.eexpr with
+			| TFunction tf ->
+				tf_stack := tf :: !tf_stack;
+				let etf = {e with eexpr = TFunction({tf with tf_expr = run f tf.tf_expr})} in
+				tf_stack := List.tl !tf_stack;
+				etf
+			| _ ->
+				check (Type.map_expr (run f) e)
+end;;

+ 10 - 0
filters.ml

@@ -863,6 +863,15 @@ let check_deprecation com =
 			()
 	) com.types
 
+let check_unification com e t =
+	begin match follow e.etype,follow t with
+		| TEnum _,TDynamic _ ->
+			add_feature com "may_print_enum";
+		| _ ->
+			()
+	end;
+	e
+
 (* PASS 1 end *)
 
 (* Saves a class state so it can be restored later, e.g. after DCE or native path rewrite *)
@@ -1138,6 +1147,7 @@ let run com tctx main =
 
 	(* PASS 1: general expression filters *)
  	let filters = [
+ 		Codegen.UnificationCallback.run (check_unification com);
 		Codegen.Abstract.handle_abstract_casts tctx;
 		blockify_ast;
 		(match com.platform with

+ 20 - 5
genjs.ml

@@ -1067,17 +1067,32 @@ let generate_enum ctx e =
 		(match f.ef_type with
 		| TFun (args,_) ->
 			let sargs = String.concat "," (List.map (fun (n,_,_) -> ident n) args) in
-			print ctx "function(%s) { var $x = [\"%s\",%d,%s]; $x.__enum__ = %s; $x.toString = $estr; return $x; }" sargs f.ef_name f.ef_index sargs p;
+			print ctx "function(%s) { var $x = [\"%s\",%d,%s]; $x.__enum__ = %s;" sargs f.ef_name f.ef_index sargs p;
+			if has_feature ctx "may_print_enum" then
+				spr ctx " $x.toString = $estr;";
+			spr ctx " return $x; }";
 			ctx.separator <- true;
 		| _ ->
 			print ctx "[\"%s\",%d]" f.ef_name f.ef_index;
 			newline ctx;
-			print ctx "%s%s.toString = $estr" p (field f.ef_name);
-			newline ctx;
+			if has_feature ctx "may_print_enum" then begin
+				print ctx "%s%s.toString = $estr" p (field f.ef_name);
+				newline ctx;
+			end;
 			print ctx "%s%s.__enum__ = %s" p (field f.ef_name) p;
 		);
 		newline ctx
 	) e.e_names;
+	if has_feature ctx "Type.allEnums" then begin
+		let ctors_without_args = List.filter (fun s ->
+			let ef = PMap.find s e.e_constrs in
+			match follow ef.ef_type with
+				| TFun _ -> false
+				| _ -> true
+		) e.e_names in
+		print ctx "%s.__empty_constructs__ = [%s]" p (String.concat "," (List.map (fun s -> Printf.sprintf "%s.%s" p s) ctors_without_args));
+		newline ctx
+	end;
 	match Codegen.build_metadata ctx.com (TEnumDecl e) with
 	| None -> ()
 	| Some e ->
@@ -1233,9 +1248,9 @@ let generate com =
 	(* TODO: fix $estr *)
 	let vars = [] in
 	let vars = (if has_feature ctx "Type.resolveClass" || has_feature ctx "Type.resolveEnum" then ("$hxClasses = " ^ (if ctx.js_modern then "{}" else "$hxClasses || {}")) :: vars else vars) in
-	let vars = (if List.exists (function TEnumDecl { e_extern = false } -> true | _ -> false) com.types
+	let vars = if has_feature ctx "may_print_enum"
 		then ("$estr = function() { return " ^ (ctx.type_accessor (TClassDecl { null_class with cl_path = ["js"],"Boot" })) ^ ".__string_rec(this,''); }") :: vars
-		else vars) in
+		else vars in
 	(match List.rev vars with
 	| [] -> ()
 	| vl ->

+ 18 - 7
matcher.ml

@@ -107,6 +107,7 @@ type matcher = {
 	mutable toplevel_or : bool;
 	mutable has_extractor : bool;
 	mutable expr_map : (int,texpr * texpr option) PMap.t;
+	mutable is_exhaustive : bool;
 }
 
 exception Not_exhaustive of pat * st
@@ -919,6 +920,8 @@ let rec compile mctx stl pmat toplevel =
 			| _ when not inf && PMap.is_empty !all ->
 				switch st_head cases
 			| [],_ when inf && not mctx.need_val && toplevel ->
+				(* ignore exhaustiveness, but mark context so we do not generate @:exhaustive metadata *)
+				mctx.is_exhaustive <- false;
 				switch st_head cases
 			| [],_ when inf ->
 				raise (Not_exhaustive(any,st_head))
@@ -970,7 +973,8 @@ let convert_con ctx con = match con.c_def with
 	| CArray i -> mk_const ctx con.c_pos (TInt (Int32.of_int i))
 	| CAny | CFields _ -> assert false
 
-let convert_switch ctx st cases loop =
+let convert_switch mctx st cases loop =
+	let ctx = mctx.ctx in
 	let e_st = convert_st ctx st in
 	let p = e_st.epos in
 	let mk_index_call () =
@@ -979,19 +983,25 @@ let convert_switch ctx st cases loop =
 		let ec = (!type_module_type_ref) ctx (TClassDecl ttype) None p in
 		let ef = mk (TField(ec, FStatic(ttype,cf))) (tfun [e_st.etype] ctx.t.tint) p in
 		let e = make_call ctx ef [e_st] ctx.t.tint p in
-		mk (TMeta((Meta.Exhaustive,[],p), e)) e.etype e.epos
+		e
+	in
+	let wrap_exhaustive e =
+		if mctx.is_exhaustive then
+			mk (TMeta((Meta.Exhaustive,[],e.epos),e)) e.etype e.epos
+		else
+			e
 	in
 	let e = match follow st.st_type with
 	| TEnum(_) ->
-		mk_index_call()
+		wrap_exhaustive (mk_index_call())
 	| TAbstract(a,pl) when (match Codegen.Abstract.get_underlying_type a pl with TEnum(_) -> true | _ -> false) ->
-		mk_index_call()
+		wrap_exhaustive (mk_index_call())
 	| TInst({cl_path = [],"Array"},_) as t ->
 		mk (TField (e_st,quick_field t "length")) ctx.t.tint p
 	| TAbstract(a,_) when Meta.has Meta.Enum a.a_meta ->
-		mk (TMeta((Meta.Exhaustive,[],p), e_st)) e_st.etype e_st.epos
+		wrap_exhaustive (e_st)
 	| TAbstract({a_path = [],"Bool"},_) ->
-		mk (TMeta((Meta.Exhaustive,[],p), e_st)) e_st.etype e_st.epos
+		wrap_exhaustive (e_st)
 	| _ ->
 		if List.exists (fun (con,_) -> match con.c_def with CEnum _ -> true | _ -> false) cases then
 			mk_index_call()
@@ -1155,6 +1165,7 @@ let match_expr ctx e cases def with_type p =
 		dt_count = 0;
 		has_extractor = has_extractor;
 		expr_map = PMap.empty;
+		is_exhaustive = true;
 	} in
 	(* flatten cases *)
 	let cases = List.map (fun (el,eg,e) ->
@@ -1371,7 +1382,7 @@ let match_expr ctx e cases def with_type p =
 	(* reindex *)
 	let rec loop dt = match dt with
 		| Goto i -> if usage.(i) > 1 then DTGoto (map.(i)) else loop (DynArray.get mctx.dt_lut i)
-		| Switch(st,cl) -> convert_switch ctx st cl loop
+		| Switch(st,cl) -> convert_switch mctx st cl loop
 		| Bind(bl,dt) -> DTBind(List.map (fun (v,st) -> v,convert_st ctx st) bl,loop dt)
 		| Expr id -> DTExpr (get_expr mctx id)
 		| Guard(id,dt1,dt2) -> DTGuard((match get_guard mctx id with Some e -> e | None -> assert false),loop dt1, match dt2 with None -> None | Some dt -> Some (loop dt))

+ 16 - 6
std/haxe/Template.hx

@@ -139,17 +139,27 @@ class Template {
 			// macro parse
 			var parp = p.pos + p.len;
 			var npar = 1;
-			while( npar > 0 ) {
+			var params = [];
+			var part = "";
+			while( true ) {
 				var c = data.charCodeAt(parp);
-				if( c == 40 )
+				parp++;
+				if( c == 40 ) {
 					npar++;
-				else if( c == 41 )
+				} else if( c == 41 ) {
 					npar--;
-				else if( c == null )
+					if (npar <= 0) break;
+				} else if( c == null ){
 					throw "Unclosed macro parenthesis";
-				parp++;
+				}
+				if ( c == 44 && npar == 1) {
+					params.push(part);
+					part = "";
+				} else {
+					part += String.fromCharCode(c);
+				}
 			}
-			var params = data.substr(p.pos+p.len,parp - (p.pos+p.len) - 1).split(",");
+			params.push(part);
 			tokens.add({ p : splitter.matched(2), s : false, l : params });
 			data = data.substr(parp,data.length - parp);
 		}

+ 1 - 1
std/js/Boot.hx

@@ -74,7 +74,7 @@ class Boot {
 			return untyped __define_feature__("js.Boot.getClass", o.__class__);
 	}
 
-	@:ifFeature("has_enum")
+	@:ifFeature("may_print_enum")
 	private static function __string_rec(o,s:String) {
 		untyped {
 			if( o == null )

+ 1 - 8
std/js/_std/Type.hx

@@ -209,14 +209,7 @@ enum ValueType {
 	}
 
 	public static function allEnums<T>( e : Enum<T> ) : Array<T> {
-		var all = [];
-		var cst : Array<String> = untyped e.__constructs__;
-		for( c in cst ) {
-			var v = Reflect.field(e,c);
-			if( !Reflect.isFunction(v) )
-				all.push(v);
-		}
-		return all;
+		return untyped e.__empty_constructs__;
 	}
 
 }

+ 13 - 0
tests/unit/issues/Issue2401.hx

@@ -0,0 +1,13 @@
+package unit.issues;
+import unit.Test;
+
+private abstract Foo(Int) from Int {
+    public function toString()
+        return 'Foo: $this';
+}
+
+class Issue2401 extends Test {
+	function test() {
+		eq("Foo: 1", Std.string((1 : Foo)));
+	}
+}

+ 19 - 0
tests/unit/issues/Issue2809.hx

@@ -0,0 +1,19 @@
+package unit.issues;
+import unit.Test;
+
+private enum MyEnum {
+	SomeValue;
+	DifferentValue;
+}
+
+class Issue2809 extends Test {
+	function test() {
+		var val:Dynamic = MyEnum.DifferentValue;
+		var x = "foo";
+		switch(val) {
+			case MyEnum.SomeValue:
+				x = "bar";
+		}
+		eq("foo", x);
+	}
+}

+ 24 - 0
tests/unit/issues/Issue2810.hx

@@ -0,0 +1,24 @@
+package unit.issues;
+import unit.Test;
+
+private abstract A(Array<Int>) {
+    public inline function new(a) {
+        this = a;
+    }
+    @:arrayAccess inline function read(i) {
+        return this[i];
+    }
+    @:arrayAccess inline function write(i,v) {
+        return this[i] = v;
+    }
+}
+
+class Issue2810 extends Test {
+	function test() {
+        var a = new A([5]);
+        var x = a[0];
+        a[0] = 6;
+        a[0] += 3;
+		eq(9, a[0]);
+	}
+}

+ 13 - 0
tests/unit/unitstd/haxe/Template.unit.hx

@@ -20,4 +20,17 @@ function myfun( resolve : String -> Dynamic, title : String, p : Int ) {
 var t1 = new haxe.Template("Call macro : $$myfun(Hello,::param::)");
 var str = t1.execute({ param : 55, mult : 2 },{ myfun : myfun });
 str == "Call macro : [Hello=110]";
+
+var mcr = {
+	a : function (resolver, s) { return "#" + s; },
+	b : function (resolver, s1, s2) { return "_" + s1 + ":" + s2; }
+};
+
+tpl = new haxe.Template( "$$b(a,$$a(b))" );
+var out3 = tpl.execute({}, mcr);
+out3 == "_a:#b";
+
+tpl = new haxe.Template( "$$a($$b(::a::,b))" );
+var out4 = tpl.execute({a:"abc"}, mcr);
+out4 == "#_abc:b";
 #end

+ 1 - 0
typer.ml

@@ -1582,6 +1582,7 @@ let rec type_binop ctx op e1 e2 is_assign_op p =
 					e, fun () -> (save(); Some (mk (TVar (v,Some ekey)) ctx.t.tvoid p))
 			in
 			let ast_call = ECall((EField(Interp.make_ast ebase,cf_get.cf_name),p),[Interp.make_ast ekey]),p in
+			let ast_call = (EMeta((Meta.PrivateAccess,[],pos ast_call),ast_call),pos ast_call) in
 			let eget = type_binop ctx op ast_call e2 true p in
 			unify ctx eget.etype r_get p;
 			let cf_set,tf_set,r_set =