浏览代码

added custom JS generator

Nicolas Cannasse 14 年之前
父节点
当前提交
6f59e12520
共有 9 个文件被更改,包括 590 次插入55 次删除
  1. 17 17
      Makefile.win
  2. 2 0
      common.ml
  3. 44 20
      genjs.ml
  4. 88 16
      interp.ml
  5. 8 1
      std/haxe/macro/Compiler.hx
  6. 305 0
      std/haxe/macro/DefaultJSGenerator.hx
  7. 52 0
      std/haxe/macro/JSGenApi.hx
  8. 27 1
      std/haxe/macro/Type.hx
  9. 47 0
      typer.ml

+ 17 - 17
Makefile.win

@@ -12,7 +12,7 @@ FILES = ast.cmx lexer.cmx type.cmx common.cmx parser.cmx typecore.cmx \
 	../neko/libs/include/ocaml/nast.cmx ../neko/libs/include/ocaml/binast.cmx ../neko/libs/include/ocaml/nxml.cmx \
 	genneko.cmx genas3.cmx genjs.cmx genswf8.cmx genswf9.cmx genswf.cmx genphp.cmx gencpp.cmx interp.cmx typer.cmx \
 	main.cmx
-	
+
 all: haxe.exe
 
 haxe.exe: $(FILES)
@@ -21,29 +21,29 @@ haxe.exe: $(FILES)
 ../neko/libs/include/ocaml/binast.cmx: ../neko/libs/include/ocaml/nast.cmx
 ../neko/libs/include/ocaml/nxml.cmx: ../neko/libs/include/ocaml/nast.cmx
 
-codegen.cmx: typeload.cmx typecore.cmx type.cmx genxml.cmx common.cmx ast.cmx 
-common.cmx: type.cmx lexer.cmx ast.cmx 
-genas3.cmx: type.cmx common.cmx codegen.cmx ast.cmx 
-gencpp.cmx: type.cmx common.cmx codegen.cmx ast.cmx 
-genjs.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx 
+codegen.cmx: typeload.cmx typecore.cmx type.cmx genxml.cmx common.cmx ast.cmx
+common.cmx: type.cmx lexer.cmx ast.cmx
+genas3.cmx: type.cmx common.cmx codegen.cmx ast.cmx
+gencpp.cmx: type.cmx common.cmx codegen.cmx ast.cmx
+genjs.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx
 genneko.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx ../neko/libs/include/ocaml/binast.cmx ../neko/libs/include/ocaml/nxml.cmx
-genphp.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx 
-genswf.cmx: type.cmx genswf9.cmx genswf8.cmx common.cmx ast.cmx 
-genswf8.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx 
-genswf9.cmx: type.cmx lexer.cmx genswf8.cmx common.cmx codegen.cmx ast.cmx 
-genxml.cmx: type.cmx lexer.cmx common.cmx ast.cmx 
-lexer.cmx: ast.cmx 
+genphp.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx
+genswf.cmx: type.cmx genswf9.cmx genswf8.cmx common.cmx ast.cmx
+genswf8.cmx: type.cmx lexer.cmx common.cmx codegen.cmx ast.cmx
+genswf9.cmx: type.cmx lexer.cmx genswf8.cmx common.cmx codegen.cmx ast.cmx
+genxml.cmx: type.cmx lexer.cmx common.cmx ast.cmx
+lexer.cmx: ast.cmx
 main.cmx: typer.cmx typeload.cmx typecore.cmx type.cmx parser.cmx \
     optimizer.cmx lexer.cmx genxml.cmx genswf.cmx genphp.cmx genneko.cmx \
     genjs.cmx gencpp.cmx genas3.cmx common.cmx codegen.cmx ast.cmx interp.cmx
-optimizer.cmx: typecore.cmx type.cmx parser.cmx common.cmx ast.cmx 
-parser.cmx: parser.ml lexer.cmx common.cmx ast.cmx 
+optimizer.cmx: typecore.cmx type.cmx parser.cmx common.cmx ast.cmx
+parser.cmx: parser.ml lexer.cmx common.cmx ast.cmx
 	(ocamlopt -pp camlp4o $(CFLAGS) -c parser.ml 2>tmp.cmi && $(OUTPUT)) || ($(OUTPUT) && exit 1)
-type.cmx: ast.cmx 
+type.cmx: ast.cmx
 typecore.cmx: type.cmx common.cmx ast.cmx
-typeload.cmx: typecore.cmx type.cmx parser.cmx common.cmx ast.cmx 
+typeload.cmx: typecore.cmx type.cmx parser.cmx common.cmx ast.cmx
 typer.cmx: typeload.cmx typecore.cmx type.cmx parser.cmx optimizer.cmx \
-    lexer.cmx common.cmx codegen.cmx ast.cmx interp.cmx
+    lexer.cmx common.cmx codegen.cmx ast.cmx interp.cmx genjs.cmx
 interp.cmx: genneko.cmx type.cmx
 
 clean:

+ 2 - 0
common.ml

@@ -73,6 +73,7 @@ type context = {
 	mutable php_front : string option;
 	mutable php_lib : string option;
 	mutable swf_libs : (string * (unit -> Swf.swf) * (unit -> ((string list * string),As3hl.hl_class) Hashtbl.t)) list;
+	mutable js_gen : (unit -> unit) option;
 	(* typing *)
 	mutable basic : basic_types;
 	mutable lines : Lexer.line_index;
@@ -108,6 +109,7 @@ let create v =
 		php_lib = None;
 		swf_libs = [];
 		js_namespace = None;
+		js_gen = None;
 		load_extern_type = [];
 		warning = (fun _ _ -> assert false);
 		error = (fun _ _ -> assert false);

+ 44 - 20
genjs.ml

@@ -34,6 +34,7 @@ type ctx = {
 	mutable handle_break : bool;
 	mutable id_counter : int;
 	mutable curmethod : (string * bool);
+	mutable type_accessor : module_type -> string;
 }
 
 let s_path ctx = function
@@ -149,7 +150,7 @@ let rec gen_call ctx e el =
 		(match ctx.current.cl_super with
 		| None -> assert false
 		| Some (c,_) ->
-			print ctx "%s.call(%s" (s_path ctx c.cl_path) (this ctx);
+			print ctx "%s.call(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 		);
@@ -157,7 +158,7 @@ let rec gen_call ctx e el =
 		(match ctx.current.cl_super with
 		| None -> assert false
 		| Some (c,_) ->
-			print ctx "%s.prototype%s.call(%s" (s_path ctx c.cl_path) (field name) (this ctx);
+			print ctx "%s.prototype%s.call(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 		);
@@ -202,7 +203,7 @@ and gen_expr ctx e =
 	| TConst c -> gen_constant ctx e.epos c
 	| TLocal s -> spr ctx (ident s)
 	| TEnumField (e,s) ->
-		print ctx "%s%s" (s_path ctx e.e_path) (field s)
+		print ctx "%s%s" (ctx.type_accessor (TEnumDecl e)) (field s)
 	| TArray (e1,e2) ->
 		gen_value ctx e1;
 		spr ctx "[";
@@ -222,7 +223,7 @@ and gen_expr ctx e =
 		gen_constant ctx e.epos (TString s);
 		spr ctx ")";
 	| TTypeExpr t ->
-		spr ctx (s_path ctx (t_path t))
+		spr ctx (ctx.type_accessor t)
 	| TParenthesis e ->
 		spr ctx "(";
 		gen_value ctx e;
@@ -286,7 +287,7 @@ and gen_expr ctx e =
 				gen_value ctx e
 		) vl;
 	| TNew (c,_,el) ->
-		print ctx "new %s(" (s_path ctx c.cl_path);
+		print ctx "new %s(" (ctx.type_accessor (TClassDecl c));
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")"
 	| TIf (cond,e,eelse) ->
@@ -374,7 +375,7 @@ and gen_expr ctx e =
 				newline ctx;
 				spr ctx "}"
 			| Some t ->
-				print ctx "if( js.Boot.__instanceof($e%d," id;
+				print ctx "if( %s.__instanceof($e%d," (ctx.type_accessor (TClassDecl { null_class with cl_path = ["js"],"Boot" })) id;
 				gen_value ctx (mk (TTypeExpr t) (mk_mono()) e.epos);
 				spr ctx ") ) {";
 				let bend = open_block ctx in
@@ -629,6 +630,16 @@ let gen_class_field ctx c f =
 		gen_value ctx e;
 		newline ctx
 
+let gen_constructor ctx e =
+	match e.eexpr with
+	| TFunction f  ->
+		let args  = List.map arg_name f.tf_args in
+		let a, args = (match args with [] -> "p" , ["p"] | x :: _ -> x, args) in
+		print ctx "function(%s) { if( %s === $_ ) return; " (String.concat "," (List.map ident args)) a;
+		gen_expr ctx (fun_block ctx f e.epos);
+		print ctx "}";
+	| _ -> assert false
+
 let generate_class ctx c =
 	ctx.current <- c;
 	ctx.curmethod <- ("new",true);
@@ -637,15 +648,7 @@ let generate_class ctx c =
 	generate_package_create ctx c.cl_path;
 	print ctx "%s = " p;
 	(match c.cl_constructor with
-	| Some { cf_expr = Some e } ->
-		(match e with
-		| { eexpr = TFunction f } ->
-			let args  = List.map arg_name f.tf_args in
-			let a, args = (match args with [] -> "p" , ["p"] | x :: _ -> x, args) in
-			print ctx "function(%s) { if( %s === $_ ) return; " (String.concat "," (List.map ident args)) a;
-			gen_expr ctx (fun_block ctx f e.epos);
-			print ctx "}";
-		| _ -> assert false)
+	| Some { cf_expr = Some e } -> gen_constructor ctx e
 	| _ -> print ctx "function() { }");
 	newline ctx;
 	print ctx "%s.__name__ = [%s]" p (String.concat "," (List.map (fun s -> Printf.sprintf "\"%s\"" (Ast.s_escape s)) (fst c.cl_path @ [snd c.cl_path])));
@@ -660,7 +663,7 @@ let generate_class ctx c =
 		newline ctx;
 	);
 	List.iter (gen_class_static_field ctx c) c.cl_ordered_statics;
-	PMap.iter (fun _ f -> match f.cf_kind with Var { v_read = AccResolve } -> () | _ -> gen_class_field ctx c f) c.cl_fields;
+	List.iter (fun f -> match f.cf_kind with Var { v_read = AccResolve } -> () | _ -> gen_class_field ctx c f) c.cl_ordered_fields;
 	print ctx "%s.prototype.__class__ = %s" p p;
 	newline ctx;
 	match c.cl_implements with
@@ -675,7 +678,8 @@ let generate_enum ctx e =
 	let ename = List.map (fun s -> Printf.sprintf "\"%s\"" (Ast.s_escape s)) (fst e.e_path @ [snd e.e_path]) in
 	print ctx "%s = { __ename__ : [%s], __constructs__ : [%s] }" p (String.concat "," ename) (String.concat "," (List.map (fun s -> Printf.sprintf "\"%s\"" s) e.e_names));
 	newline ctx;
-	PMap.iter (fun _ f ->
+	List.iter (fun n ->
+		let f = PMap.find n e.e_constrs in
 		print ctx "%s%s = " p (field f.ef_name);
 		(match f.ef_type with
 		| TFun (args,_) ->
@@ -689,7 +693,7 @@ let generate_enum ctx e =
 			print ctx "%s%s.__enum__ = %s" p (field f.ef_name) p;
 		);
 		newline ctx
-	) e.e_constrs;
+	) e.e_names;
 	match Codegen.build_metadata ctx.com (TEnumDecl e) with
 	| None -> ()
 	| Some e ->
@@ -713,7 +717,7 @@ let generate_type ctx = function
 	| TEnumDecl e -> generate_enum ctx e
 	| TTypeDecl _ -> ()
 
-let generate com =
+let alloc_ctx com =
 	let ctx = {
 		com = com;
 		stack = Codegen.stack_init com false;
@@ -729,8 +733,28 @@ let generate com =
 		handle_break = false;
 		id_counter = 0;
 		curmethod = ("",false);
+		type_accessor = (fun _ -> assert false);
 	} in
+	ctx.type_accessor <- (fun t -> s_path ctx (t_path t));
+	ctx
+
+let gen_single_expr ctx e constr =
+	if constr then gen_constructor ctx e else gen_expr ctx e;
+	let str = Buffer.contents ctx.buf in
+	Buffer.reset ctx.buf;
+	ctx.id_counter <- 0;
+	str
+
+let set_debug_infos ctx c m s =
+	ctx.current <- c;
+	ctx.curmethod <- (m,s)
+
+let generate com =
 	let t = Common.timer "generate js" in
+	(match com.js_gen with
+	| Some g -> g()
+	| None ->
+	let ctx = alloc_ctx com in
 	print ctx "$estr = function() { return js.Boot.__string_rec(this,''); }";
 	newline ctx;
 	(match ctx.namespace with
@@ -766,6 +790,6 @@ let generate com =
 	| Some e -> gen_expr ctx e);
 	let ch = open_out_bin com.file in
 	output_string ch (Buffer.contents ctx.buf);
-	close_out ch;
+	close_out ch);
 	t()
 

+ 88 - 16
interp.ml

@@ -54,6 +54,8 @@ and vabstract =
 	| AZipD of zlib
 	| AUtf8 of UTF8.Buf.buf
 	| ASocket of Unix.file_descr
+	| ATExpr of texpr
+	| ATDecl of module_type
 
 and vfunction =
 	| Fun0 of (unit -> value)
@@ -93,6 +95,7 @@ type extern_api = {
 	typeof : Ast.expr -> Type.t;
 	type_patch : string -> string -> bool -> string option -> unit;
 	meta_patch : string -> string -> string option -> bool -> unit;
+	set_js_generator : (value -> unit) -> unit;
 }
 
 type context = {
@@ -102,7 +105,7 @@ type context = {
 	globals : (string, value) Hashtbl.t;
 	prototypes : (string list, vobject) Hashtbl.t;
 	mutable error : bool;
-	mutable enums : string array array;
+	mutable enums : (value * string) array array;
 	mutable do_call : value -> value -> value list -> pos -> value;
 	mutable do_string : value -> string;
 	mutable do_loadprim : value -> value -> value;
@@ -408,7 +411,7 @@ let builtins =
 			match vl with
 			| VFunction f :: _ :: _ ->
 				VClosure (vl, do_closure)
-			| _ -> exc (VString "Invalid closure arguments number")
+			| _ -> exc (VString "Can't create closure : value is not a function")
 		);
 		"apply", FunVar (fun vl ->
 			match vl with
@@ -1642,6 +1645,16 @@ let macro_lib =
 			| _ -> error());
 			VNull
 		);
+		"custom_js", Fun1 (fun f ->
+			match f with
+			| VFunction (Fun1 _) ->
+				let ctx = get_ctx() in
+				ctx.curapi.set_js_generator (fun api ->
+					ignore(catch_errors ctx (fun() -> ctx.do_call VNull f [api] null_pos));
+				);
+				VNull
+			| _ -> error()
+		);
 	]
 
 (* ---------------------------------------------------------------------- *)
@@ -2283,6 +2296,9 @@ type enum_index =
 	| ICType
 	| IField
 	| IType
+	| IFieldKind
+	| IMethodKind
+	| IVarAccess
 
 let enum_name = function
 	| IExpr -> "ExprDef"
@@ -2293,21 +2309,26 @@ let enum_name = function
 	| ICType -> "ComplexType"
 	| IField -> "FieldType"
 	| IType -> "Type"
+	| IFieldKind -> "FieldKind"
+	| IMethodKind -> "MethodKind"
+	| IVarAccess -> "VarAccess"
 
 let init ctx =
-	let enums = [IExpr;IBinop;IUnop;IConst;ITParam;ICType;IField;IType] in
+	let enums = [IExpr;IBinop;IUnop;IConst;ITParam;ICType;IField;IType;IFieldKind;IMethodKind;IVarAccess] in
 	let get_enum_proto e =
-		match get_path ctx ["haxe";"macro";enum_name e;"__constructs__"] null_pos with
-		| VObject cst ->
-			(match get_field cst "__a" with
+		match get_path ctx ["haxe";"macro";enum_name e] null_pos with
+		| VObject e ->
+			(match get_field e "__constructs__" with
+			| VObject cst ->
+				(match get_field cst "__a" with
 				| VArray a ->
 					Array.map (fun s ->
 						match s with
-						| VObject s -> (match get_field s "__s" with VString s -> s | _ -> assert false)
+						| VObject s -> (match get_field s "__s" with VString s -> get_field e s,s | _ -> assert false)
 						| _ -> assert false
 					) a
-				| _ -> assert false
-			)
+				| _ -> assert false)
+			| _ -> assert false)
 		| _ -> assert false
 	in
 	ctx.enums <- Array.of_list (List.map get_enum_proto enums)
@@ -2358,13 +2379,16 @@ let enc_hash h =
 let enc_obj l = VObject (obj l)
 
 let enc_enum (i:enum_index) index pl =
-	let eindex : int = Obj.magic i in
-	let etags = (get_ctx()).enums.(eindex) in
-	enc_inst ["haxe";"macro";enum_name i] [
-		"tag", VString etags.(index);
-		"index", VInt index;
-		"args", VArray (Array.of_list pl);
-	]
+	let eindex : int = Obj.magic i in	
+	let edef = (get_ctx()).enums.(eindex) in
+	if pl = [] then
+		fst edef.(index)
+	else
+		enc_inst ["haxe";"macro";enum_name i] [
+			"tag", VString (snd edef.(index));
+			"index", VInt index;
+			"args", VArray (Array.of_list pl);
+		]
 
 let encode_const c =
 	let tag, pl = match c with
@@ -2829,6 +2853,7 @@ let encode_meta m set =
 
 let rec encode_tenum e =
 	enc_obj [
+		"__t", encode_tdecl (TEnumDecl e);
 		"pack", enc_array (List.map enc_string (fst e.e_path));
 		"name", enc_string (snd e.e_path);
 		"pos", encode_pos e.e_pos;
@@ -2857,10 +2882,41 @@ and encode_cfield f =
 		"isPublic", VBool f.cf_public;
 		"params", enc_array (List.map (fun (n,t) -> enc_obj ["name",enc_string n;"t",encode_type t]) f.cf_params);
 		"meta", encode_meta f.cf_meta (fun m -> f.cf_meta <- m);
+		"expr", (match f.cf_expr with None -> VNull | Some e -> encode_texpr e);
+		"kind", encode_field_kind f.cf_kind;
 	]
 
+and encode_field_kind k =
+	let tag, pl = (match k with
+		| Type.Var v -> 0, [encode_var_access v.v_read; encode_var_access v.v_write]
+		| Method m -> 1, [encode_method_kind m]
+	) in
+	enc_enum IFieldKind tag pl
+
+and encode_var_access a =
+	let tag, pl = (match a with
+		| AccNormal -> 0, []
+		| AccNo -> 1, []
+		| AccNever -> 2, []
+		| AccResolve -> 3, []
+		| AccCall s -> 4, [enc_string s]
+		| AccInline	-> 5, []
+		| AccRequire s -> 6, [enc_string s]
+	) in
+	enc_enum IVarAccess tag pl
+
+and encode_method_kind m =
+	let tag, pl = (match m with
+		| MethNormal -> 0, []
+		| MethInline -> 1, []
+		| MethDynamic -> 2, []
+		| MethMacro -> 3, []
+	) in
+	enc_enum IMethodKind tag pl
+
 and encode_tclass c =
 	enc_obj [
+		"__t", encode_tdecl (TClassDecl c);
 		"pack", enc_array (List.map enc_string (fst c.cl_path));
 		"name", enc_string (snd c.cl_path);
 		"pos", encode_pos c.cl_pos;
@@ -2878,10 +2934,12 @@ and encode_tclass c =
 		"statics", encode_ref c.cl_ordered_statics (encode_array encode_cfield) (fun() -> "class fields");
 		"constructor", (match c.cl_constructor with None -> VNull | Some c -> encode_ref c encode_cfield (fun() -> "constructor"));
 		"meta", encode_meta c.cl_meta (fun m -> c.cl_meta <- m);
+		"init", (match c.cl_init with None -> VNull | Some e -> encode_texpr e);
 	]
 
 and encode_ttype t =
 	enc_obj [
+		"__t", encode_tdecl (TTypeDecl t);
 		"pack", enc_array (List.map enc_string (fst t.t_path));
 		"name", enc_string (snd t.t_path);
 		"pos", encode_pos t.t_pos;
@@ -2893,6 +2951,9 @@ and encode_ttype t =
 		"meta", encode_meta t.t_meta (fun m -> t.t_meta <- m);
 	]
 
+and encode_tdecl t =
+	VAbstract (ATDecl t)
+
 and encode_tanon a =
 	enc_obj [
 		"fields", encode_pmap_array encode_cfield a.a_fields;
@@ -2938,6 +2999,17 @@ and encode_type t =
 	let tag, pl = loop t in
 	enc_enum IType tag pl
 
+and encode_texpr e =
+	VAbstract (ATExpr e)
+
+let decode_tdecl v =
+	match v with
+	| VObject o ->
+		(match get_field o "__t" with
+		| VAbstract (ATDecl t) -> t
+		| _ -> raise Invalid_expr)
+	| _ -> raise Invalid_expr
+
 (* ---------------------------------------------------------------------- *)
 (* VALUE-TO-CONSTANT *)
 

+ 8 - 1
std/haxe/macro/Compiler.hx

@@ -169,7 +169,7 @@ class Compiler {
 		} catch( e : haxe.io.Eof ) {
 		}
 	}
-	
+
 	/**
 		Mark a class (or array of classes) with the metadata @:keep
 	**/
@@ -183,6 +183,13 @@ class Compiler {
 			addMetadata("@:keep", cl);
 	}
 
+	/**
+		Change the default JS output by using a custom generator callback
+	**/
+	public static function setCustomJSGenerator( callb : JSGenApi -> Void ) {
+		load("custom_js",1)(callb);
+	}
+
 	static function load( f, nargs ) : Dynamic {
 		#if macro
 		return neko.Lib.load("macro", f, nargs);

+ 305 - 0
std/haxe/macro/DefaultJSGenerator.hx

@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2005-2010, The haXe Project Contributors
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+package haxe.macro;
+import haxe.macro.Type;
+import haxe.macro.Expr;
+using Lambda;
+
+class DefaultJSGenerator {
+
+	var api : JSGenApi;
+	var buf : StringBuf;
+	var inits : List<TypedExpr>;
+	var statics : List<{ c : ClassType, f : ClassField }>;
+	var packages : Hash<Bool>;
+	var forbidden : Hash<Bool>;
+
+	public function new(api) {
+		this.api = api;
+		buf = new StringBuf();
+		inits = new List();
+		statics = new List();
+		packages = new Hash();
+		forbidden = new Hash();
+		for( x in ["prototype", "__proto__", "constructor"] )
+			forbidden.set(x, true);
+		api.setTypeAccessor(getType);
+	}
+	
+	function getType( t : Type ) {
+		return switch(t) {
+			case TInst(c, _): getPath(c.get());
+			case TEnum(e, _): getPath(e.get());
+			default: throw "assert";
+		};
+	}
+
+	inline function print(str) {
+		buf.add(str);
+	}
+	
+	inline function newline() {
+		buf.add(";\n");
+	}
+
+	inline function genExpr(e) {
+		print(api.generateExpr(e));
+	}
+	
+	@:macro static function fprint( e : Expr ) {
+		switch( e.expr ) {
+		case EConst(c):
+			switch( c ) {
+			case CString(str):
+				var exprs = [];
+				var r = ~/%((\([^\)]+\))|([A-Za-z_][A-Za-z0-9_]*))/;
+				var pos = e.pos;
+				while( r.match(str) ) {
+					exprs.push({ expr : EConst(CString(r.matchedLeft())), pos : pos });
+					var v = r.matched(1);
+					if( v.charCodeAt(0) == "(".code )
+						exprs.push(Context.parse(v.substr(1, v.length-2), pos));
+					else
+						exprs.push({ expr : EConst(CIdent(v)), pos : pos });
+					str = r.matchedRight();
+				}
+				exprs.push({ expr : EConst(CString(str)), pos : pos });
+				var ret = null;
+				for( e in exprs )
+					if( ret == null ) ret = e else ret = { expr : EBinop(OpAdd, ret, e), pos : pos };
+				return { expr : ECall({ expr : EConst(CIdent("print")), pos : pos },[ret]), pos : pos };
+			default:
+			}
+		default:
+		}
+		Context.error("Expression should be a constant string", e.pos);
+		return null;
+	}
+	
+	function field(p) {
+		return api.isKeyword(p) ? '["' + p + '"]' : "." + p;
+	}
+	
+	function genPackage( p : Array<String> ) {
+		var full = null;
+		for( x in p ) {
+			var prev = full;
+			if( full == null ) full = x else full += "." + x;
+			if( packages.exists(full) )
+				continue;
+			packages.set(full, true);
+			if( prev == null )
+				fprint("if(typeof %x=='undefined') %x = {}");
+			else {
+				var p = prev + field(x);
+				fprint("if(!%p) %p = {}");
+			}
+			newline();
+		}
+	}
+	
+	function getPath( t : BaseType ) {
+		return (t.pack.length == 0) ? t.name : t.pack.join(".") + "." + t.name;
+	}
+	
+	function checkFieldName( c : ClassType, f : ClassField ) {
+		if( forbidden.exists(f.name) )
+			Context.error("The field " + f.name + " is not allowed in JS", c.pos);
+	}
+	
+	function genClassField( c : ClassType, p : String, f : ClassField ) {
+		checkFieldName(c, f);
+		var field = field(f.name);
+		fprint("%p.prototype%field = ");
+		if( f.expr == null )
+			print("null");
+		else {
+			api.setDebugInfos(c, f.name, false);
+			print(api.generateExpr(f.expr));
+		}
+		newline();
+	}
+
+	function genStaticField( c : ClassType, p : String, f : ClassField ) {
+		checkFieldName(c, f);
+		var field = field(f.name);
+		if( f.expr == null ) {
+			fprint("%p%field = null");
+			newline();
+		} else switch( f.kind ) {
+		case FMethod(_):
+			fprint("%p%field = ");
+			api.setDebugInfos(c, f.name, true);
+			genExpr(f.expr);
+			newline();
+		default:
+			statics.add( { c : c, f : f } );
+		}
+	}
+	
+	function genClass( c : ClassType ) {
+		genPackage(c.pack);
+		var p = getPath(c);
+		fprint("%p = ");
+		api.setDebugInfos(c, "new", false);
+		if( c.constructor != null )
+			api.generateConstructor(c.constructor.get().expr);
+		else
+			print("function() { }");
+		newline();
+		var name = p.split(".").map(api.quoteString).join(",");
+		fprint("%p.__name__ = [%name]");
+		newline();
+		if( c.superClass != null ) {
+			var psup = getPath(c.superClass.t.get());
+			fprint("%p.__super__ = %psup");
+			newline();
+			fprint("for(var k in %psup.prototype ) %p.prototype[k] = %psup.prototype[k]");
+		}
+		for( f in c.statics.get() )
+			genStaticField(c, p, f);
+		for( f in c.fields.get() ) {
+			switch( f.kind ) {
+			case FVar(r, _):
+				if( r == AccResolve ) continue;
+			default:
+			}
+			genClassField(c, p, f);
+		}
+		fprint("%p.prototype.__class__ = %p");
+		newline();
+		if( c.interfaces.length > 0 ) {
+			var me = this;
+			var inter = c.interfaces.map(function(i) return me.getPath(i.t.get())).join(",");
+			fprint("%p.prototype = [%inter]");
+			newline();
+		}
+	}
+	
+	function genEnum( e : EnumType ) {
+		genPackage(e.pack);
+		var p = getPath(e);
+		var names = p.split(".").map(api.quoteString).join(",");
+		var constructs = e.names.map(api.quoteString).join(",");
+		fprint("%p = { __ename__ : [%names], __constructs__ : [%constructs] }");
+		newline();
+		for( c in e.contructs.keys() ) {
+			var c = e.contructs.get(c);
+			var f = field(c.name);
+			fprint("%p%f = ");
+			switch( c.type ) {
+			case TFun(args, _):
+				var sargs = args.map(function(a) return a.name).join(",");
+				fprint('function(%sargs) { var $x = ["%(c.name)",%(c.index),%sargs]; $x.__enum__ = %p; $x.toString = $estr; return $x; }');
+			default:
+				print("[" + api.quoteString(c.name) + "," + c.index + "]");
+				newline();
+				fprint("%p%f.toString = $estr");
+				newline();
+				fprint("%p%f.__enum__ = %p");
+			}
+			newline();
+		}
+		var meta = api.buildMetaData(e);
+		if( meta != null ) {
+			fprint("%p.__meta__ = ");
+			genExpr(meta);
+			newline();
+		}
+	}
+	
+	
+	function genStaticValue( c : ClassType, cf : ClassField ) {
+		var p = getPath(c);
+		var f = field(cf.name);
+		fprint("%p%f = ");
+		genExpr(cf.expr);
+		newline();
+	}
+	
+	function genType( t : Type ) {
+		switch( t ) {
+		case TInst(c, _):
+			var c = c.get();
+			if( c.init != null )
+				inits.add(c.init);
+			if( !c.isExtern ) genClass(c);
+		case TEnum(r, _):
+			var e = r.get();
+			if( !e.isExtern ) genEnum(e);
+		default:
+		}
+	}
+
+	public function generate() {
+		print("$estr = function() { return js.Boot.__string_rec(this,''); }");
+		newline();
+		/*
+		(match ctx.namespace with
+		| None -> ()
+		| Some ns ->
+			print ctx "if(typeof %s=='undefined') %s = {}" ns ns;
+			newline ctx);
+		*/
+		for( t in api.types )
+			genType(t);
+		print("$_ = {}");
+		newline();
+		print("js.Boot.__res = {}");
+		newline();
+		/*
+		if com.debug then begin
+			print ctx "%s = []" ctx.stack.Codegen.stack_var;
+			newline ctx;
+			print ctx "%s = []" ctx.stack.Codegen.stack_exc_var;
+			newline ctx;
+		end;
+		*/
+		print("js.Boot.__init()");
+		newline();
+		for( e in inits ) {
+			genExpr(e);
+			newline();
+		}
+		for( s in statics ) {
+			genStaticValue(s.c,s.f);
+			newline();
+		}
+		if( api.main != null ) {
+			genExpr(api.main);
+			newline();
+		}
+		var file = neko.io.File.write(api.outputFile, true);
+		file.writeString(buf.toString());
+		file.close();
+	}
+	
+	#if macro
+	public static function use() {
+		Compiler.setCustomJSGenerator(function(api) new DefaultJSGenerator(api).generate());
+	}
+	#end
+
+}

+ 52 - 0
std/haxe/macro/JSGenApi.hx

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2005-2010, The haXe Project Contributors
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+package haxe.macro;
+import haxe.macro.Type;
+
+/**
+	This is the api that is passed to the custom JS generator.
+**/
+typedef JSGenApi = {
+	/** the file in which the JS code can be generated **/
+	var outputFile : String;
+	/** all the types that were compiled by haXe **/
+	var types : Array<Type>;
+	/** the main call expression, if a -main class is defined **/
+	var main : Null<TypedExpr>;
+	/** generate the JS code for a given typed expression **/
+	function generateExpr( e : TypedExpr ) : String;
+	/** define the JS code that gets generated when a class or enum is accessed in a typed expression **/
+	function setTypeAccessor( callb : Type -> String ) : Void;
+	/** tells if the given identifier is a JS keyword **/
+	function isKeyword( ident : String ) : Bool;
+	/** quote and escape the given string constant **/
+	function quoteString( s : String ) : String;
+	/** create the metadata expression for the given type **/
+	function buildMetaData( t : BaseType ) : Null<TypedExpr>;
+	/** set the current class/method for debug stack management **/
+	function setDebugInfos( c : ClassType, meth : String, isStatic : Bool ) : Void;
+	/** generate the JS code for a given class constructor **/
+	function generateConstructor( e : TypedExpr ) : String;
+}

+ 27 - 1
std/haxe/macro/Type.hx

@@ -61,7 +61,8 @@ typedef ClassField = {
 	var isPublic : Bool;
 	var params : Array<{ name : String, t : Type }>;
 	var meta : Metadata;
-	//var kind : FieldKind;
+	var kind : FieldKind;
+	var expr : Null<TypedExpr>;
 }
 
 typedef ClassType = {> BaseType,
@@ -74,6 +75,7 @@ typedef ClassType = {> BaseType,
 	//var dynamic : Null<Type>;
 	//var arrayAccess : Null<Type>;
 	var constructor : Null<Ref<ClassField>>;
+	var init : Null<TypedExpr>;
 }
 
 typedef EnumField = {
@@ -98,3 +100,27 @@ typedef Metadata = {
 	function add( name : String, params : Array<Expr>, pos : Expr.Position ) : Void;
 	function remove( name : String ) : Void;
 }
+
+enum FieldKind {
+	FVar( read : VarAccess, write : VarAccess );
+	FMethod( k : MethodKind );
+}
+
+enum VarAccess {
+	AccNormal;
+	AccNo;
+	AccNever;
+	AccResolve;
+	AccCall( m : String );
+	AccInline;
+	AccRequire( r : String );
+}
+
+enum MethodKind {
+	MethNormal;
+	MethInline;
+	MethDynamic;
+	MethMacro;
+}
+
+extern enum TypedExpr {}

+ 47 - 0
typer.ml

@@ -1926,6 +1926,53 @@ let make_macro_api ctx p =
 		Interp.print = (fun s ->
 			if not ctx.com.display then print_string s
 		);
+		Interp.set_js_generator = (fun gen ->
+			let js_ctx = Genjs.alloc_ctx ctx.com in
+			ctx.com.js_gen <- Some (fun() ->
+				let ctx = Interp.enc_obj [
+					"outputFile", Interp.enc_string ctx.com.file;
+					"types", Interp.enc_array (List.map (fun t -> Interp.encode_type (make_instance t)) ctx.com.types);
+					"generateExpr", Interp.VFunction (Interp.Fun1 (fun v ->
+						match v with
+						| Interp.VAbstract (Interp.ATExpr e) ->
+							let str = Genjs.gen_single_expr js_ctx e false in
+							Interp.enc_string str
+						| _ -> failwith "Invalid expression";
+					));
+					"isKeyword", Interp.VFunction (Interp.Fun1 (fun v ->
+						Interp.VBool (Hashtbl.mem Genjs.kwds (Interp.dec_string v))
+					));
+					"quoteString", Interp.VFunction (Interp.Fun1 (fun v ->
+						Interp.enc_string ("\"" ^ Ast.s_escape (Interp.dec_string v) ^ "\"")
+					));
+					"buildMetaData", Interp.VFunction (Interp.Fun1 (fun t ->
+						match Codegen.build_metadata ctx.com (Interp.decode_tdecl t) with
+						| None -> Interp.VNull
+						| Some e -> Interp.encode_texpr e
+					));
+					"setDebugInfos", Interp.VFunction (Interp.Fun3 (fun c m s ->
+						Genjs.set_debug_infos js_ctx (match Interp.decode_tdecl c with TClassDecl c -> c | _ -> assert false) (Interp.dec_string m) (Interp.dec_bool s);
+						Interp.VNull
+					));
+					"generateConstructor", Interp.VFunction (Interp.Fun1 (fun v ->
+						match v with
+						| Interp.VAbstract (Interp.ATExpr e) ->
+							let str = Genjs.gen_single_expr js_ctx e true in
+							Interp.enc_string str
+						| _ -> failwith "Invalid expression";
+					));
+					"setTypeAccessor", Interp.VFunction (Interp.Fun1 (fun callb ->
+						js_ctx.Genjs.type_accessor <- (fun t -> 
+							let v = Interp.encode_type (make_instance t) in
+							let ret = Interp.call (Interp.get_ctx()) Interp.VNull callb [v] Nast.null_pos in
+							Interp.dec_string ret
+						);
+						Interp.VNull
+					));
+				] in
+				gen ctx
+			);
+		);
 	}
 
 let load_macro ctx cpath f p =