Browse Source

use `@:value` to generate more accurate XML information (closes #3725)

Simon Krajewski 10 years ago
parent
commit
a9852f86e8
5 changed files with 128 additions and 26 deletions
  1. 33 25
      genxml.ml
  2. 1 0
      std/haxe/rtti/CType.hx
  3. 3 0
      std/haxe/rtti/Rtti.hx
  4. 2 1
      std/haxe/rtti/XmlParser.hx
  5. 89 0
      tests/unit/src/unit/issues/Issue3725.hx

+ 33 - 25
genxml.ml

@@ -92,7 +92,7 @@ let gen_meta meta =
 		) meta in
 		) meta in
 		[node "meta" [] nodes]
 		[node "meta" [] nodes]
 
 
-let rec gen_type ?(tfunc=None) t =
+let rec gen_type ?(values=None) t =
 	match t with
 	match t with
 	| TMono m -> (match !m with None -> tag "unknown" | Some t -> gen_type t)
 	| TMono m -> (match !m with None -> tag "unknown" | Some t -> gen_type t)
 	| TEnum (e,params) -> gen_type_decl "e" (TEnumDecl e) params
 	| TEnum (e,params) -> gen_type_decl "e" (TEnumDecl e) params
@@ -100,29 +100,30 @@ let rec gen_type ?(tfunc=None) t =
 	| TAbstract (a,params) -> gen_type_decl "x" (TAbstractDecl a) params
 	| TAbstract (a,params) -> gen_type_decl "x" (TAbstractDecl a) params
 	| TType (t,params) -> gen_type_decl "t" (TTypeDecl t) params
 	| TType (t,params) -> gen_type_decl "t" (TTypeDecl t) params
 	| TFun (args,r) ->
 	| TFun (args,r) ->
-		let s_const ct = match ct with
-			| TString s -> Printf.sprintf "'%s'" (Ast.s_escape s)
-			| _ -> s_const ct
-		in
 		let names = String.concat ":" (List.map gen_arg_name args) in
 		let names = String.concat ":" (List.map gen_arg_name args) in
-		let values = match tfunc with
-			| None ->
-				[]
-			| Some tfunc ->
+		let values = match values with
+			| None -> []
+			| Some values ->
 				let has_value = ref false in
 				let has_value = ref false in
-				let values = List.map (fun (_,cto) -> match cto with
-					| None ->
-						""
-					| Some ct ->
+				let values = List.map (fun (n,_,_) ->
+					try
+						let e = PMap.find n values in
 						has_value := true;
 						has_value := true;
-						s_const ct
-				) tfunc.tf_args in
+						let s = Ast.s_expr e in
+						(* the XML parser has issues otherwise *)
+						String.concat "'" (ExtString.String.nsplit s "\"")
+					with Not_found ->
+						""
+				) args in
 				if !has_value then
 				if !has_value then
 					["v",String.concat ":" values]
 					["v",String.concat ":" values]
 				else
 				else
 					[]
 					[]
 		in
 		in
-		node "f" (("a",names) :: values) (List.map gen_type (List.map (fun (_,opt,t) -> if opt then follow_param t else t) args @ [r]))
+		let args = List.map (fun (_,opt,t) ->
+			if opt then follow_param t else t
+		) args in
+		node "f" (("a",names) :: values) (List.map gen_type (args @ [r]))
 	| TAnon a -> node "a" [] (pmap (fun f -> gen_field [] { f with cf_public = false }) a.a_fields)
 	| TAnon a -> node "a" [] (pmap (fun f -> gen_field [] { f with cf_public = false }) a.a_fields)
 	| TDynamic t2 -> node "d" [] (if t == t2 then [] else [gen_type t2])
 	| TDynamic t2 -> node "d" [] (if t == t2 then [] else [gen_type t2])
 	| TLazy f -> gen_type (!f())
 	| TLazy f -> gen_type (!f())
@@ -140,23 +141,30 @@ and gen_field att f =
 		| AccInline -> (name,"inline") :: att
 		| AccInline -> (name,"inline") :: att
 	in
 	in
 	let att = (match f.cf_expr with None -> att | Some e -> ("line",string_of_int (Lexer.get_error_line e.epos)) :: att) in
 	let att = (match f.cf_expr with None -> att | Some e -> ("line",string_of_int (Lexer.get_error_line e.epos)) :: att) in
-	let att = (match f.cf_kind with
-		| Var v -> add_get_set v.v_read "get" (add_get_set v.v_write "set" att)
+	let att,values = (match f.cf_kind with
+		| Var v ->
+			let att = try
+				begin match Meta.get Meta.Value f.cf_meta with
+					| (_,[e],_) -> ("expr",String.concat "'" (ExtString.String.nsplit (Ast.s_expr e) "\"")) :: att
+					| _ -> att
+				end
+			with Not_found ->
+				att
+			in
+			add_get_set v.v_read "get" (add_get_set v.v_write "set" att),PMap.empty
 		| Method m ->
 		| Method m ->
-			(match m with
+			let att = match m with
 			| MethNormal | MethMacro -> ("set", "method") :: att
 			| MethNormal | MethMacro -> ("set", "method") :: att
 			| MethDynamic -> ("set", "dynamic") :: att
 			| MethDynamic -> ("set", "dynamic") :: att
-			| MethInline -> ("get", "inline") :: ("set","null") :: att)
+			| MethInline -> ("get", "inline") :: ("set","null") :: att
+			in
+			att,get_value_meta f.cf_meta
 	) in
 	) in
 	let att = (match f.cf_params with [] -> att | l -> ("params", String.concat ":" (List.map (fun (n,_) -> n) l)) :: att) in
 	let att = (match f.cf_params with [] -> att | l -> ("params", String.concat ":" (List.map (fun (n,_) -> n) l)) :: att) in
 	let overloads = match List.map (gen_field []) f.cf_overloads with
 	let overloads = match List.map (gen_field []) f.cf_overloads with
 		| [] -> []
 		| [] -> []
 		| nl -> [node "overloads" [] nl]
 		| nl -> [node "overloads" [] nl]
 	in
 	in
-	let tfunc = match f.cf_expr with
-		| Some ({eexpr = TFunction tf}) -> Some tf
-		| _ -> None
-	in
 	let field_name cf =
 	let field_name cf =
 		try
 		try
 			begin match Meta.get Meta.RealPath cf.cf_meta with
 			begin match Meta.get Meta.RealPath cf.cf_meta with
@@ -166,7 +174,7 @@ and gen_field att f =
 		with Not_found ->
 		with Not_found ->
 			cf.cf_name
 			cf.cf_name
 	in
 	in
-	node (field_name f) (if f.cf_public then ("public","1") :: att else att) (gen_type ~tfunc:tfunc f.cf_type :: gen_meta f.cf_meta @ gen_doc_opt f.cf_doc @ overloads)
+	node (field_name f) (if f.cf_public then ("public","1") :: att else att) (gen_type ~values:(Some values) f.cf_type :: gen_meta f.cf_meta @ gen_doc_opt f.cf_doc @ overloads)
 
 
 let gen_constr e =
 let gen_constr e =
 	let doc = gen_doc_opt e.ef_doc in
 	let doc = gen_doc_opt e.ef_doc in

+ 1 - 0
std/haxe/rtti/CType.hx

@@ -69,6 +69,7 @@ typedef ClassField = {
 	var meta : MetaData;
 	var meta : MetaData;
 	var line : Null<Int>;
 	var line : Null<Int>;
 	var overloads : Null<List<ClassField>>;
 	var overloads : Null<List<ClassField>>;
+	var expr : Null<String>;
 }
 }
 
 
 typedef TypeInfos = {
 typedef TypeInfos = {

+ 3 - 0
std/haxe/rtti/Rtti.hx

@@ -38,6 +38,9 @@ class Rtti {
 	**/
 	**/
 	static public function getRtti<T>(c:Class<T>):Classdef {
 	static public function getRtti<T>(c:Class<T>):Classdef {
 		var rtti = Reflect.field(c, "__rtti");
 		var rtti = Reflect.field(c, "__rtti");
+		if (rtti == null) {
+			throw 'Class ${Type.getClassName(c)} has no RTTI information, consider adding @:rtti';
+		}
 		var x = Xml.parse(rtti).firstElement();
 		var x = Xml.parse(rtti).firstElement();
 		var infos = new haxe.rtti.XmlParser().processElement(x);
 		var infos = new haxe.rtti.XmlParser().processElement(x);
 		switch (infos) {
 		switch (infos) {

+ 2 - 1
std/haxe/rtti/XmlParser.hx

@@ -418,7 +418,8 @@ class XmlParser {
 			params : if( x.has.params ) mkTypeParams(x.att.params) else [],
 			params : if( x.has.params ) mkTypeParams(x.att.params) else [],
 			platforms : defplat(),
 			platforms : defplat(),
 			meta : meta,
 			meta : meta,
-			overloads: overloads
+			overloads: overloads,
+			expr : if( x.has.expr ) x.att.expr else null
 		};
 		};
 	}
 	}
 
 

+ 89 - 0
tests/unit/src/unit/issues/Issue3725.hx

@@ -0,0 +1,89 @@
+package unit.issues;
+
+@:rtti
+class Issue3725 extends Test {
+
+	static function myStaticArgs(stringValue = "foo", intValue = 12, intHexValue = 0xFFFFFF, floatValue = 12.2223, boolValue = true) { }
+
+	function testStaticArgs() {
+		var rtti = haxe.rtti.Rtti.getRtti(Issue3725);
+		var valueMap = new Map();
+		for (cf in rtti.statics) {
+			if (cf.name == "myStaticArgs") {
+				switch (cf.type) {
+					case CFunction(args, _):
+						for (arg in args) {
+							valueMap[arg.name] = arg.value;
+						}
+					case _:
+				}
+			}
+		}
+		eq("'foo'", valueMap["stringValue"]);
+		eq("12", valueMap["intValue"]);
+		eq("0xFFFFFF", valueMap["intHexValue"]);
+		eq("12.2223", valueMap["floatValue"]);
+		eq("true", valueMap["boolValue"]);
+	}
+
+	static var stringValue = "foo";
+	static var intValue = 12;
+	static var intHexValue = 0xFFFFFF;
+	static var floatValue = 12.2223;
+	static var boolValue = true;
+
+	function testStaticFields() {
+		var rtti = haxe.rtti.Rtti.getRtti(Issue3725);
+		var valueMap = new Map();
+		for (cf in rtti.statics) {
+			valueMap[cf.name] = cf.expr;
+		}
+		eq("'foo'", valueMap["stringValue"]);
+		eq("12", valueMap["intValue"]);
+		eq("0xFFFFFF", valueMap["intHexValue"]);
+		eq("12.2223", valueMap["floatValue"]);
+		eq("true", valueMap["boolValue"]);
+	}
+
+	function myMemberArgs(stringValue = "foo", intValue = 12, intHexValue = 0xFFFFFF, floatValue = 12.2223, boolValue = true) { }
+
+	function testMemberArgs() {
+		var rtti = haxe.rtti.Rtti.getRtti(Issue3725);
+		var valueMap = new Map();
+		for (cf in rtti.fields) {
+			if (cf.name == "myMemberArgs") {
+				switch (cf.type) {
+					case CFunction(args, _):
+						for (arg in args) {
+							valueMap[arg.name] = arg.value;
+						}
+					case _:
+				}
+			}
+		}
+		eq("'foo'", valueMap["stringValue"]);
+		eq("12", valueMap["intValue"]);
+		eq("0xFFFFFF", valueMap["intHexValue"]);
+		eq("12.2223", valueMap["floatValue"]);
+		eq("true", valueMap["boolValue"]);
+	}
+
+	var stringValueM = "foo";
+	var intValueM = 12;
+	var intHexValueM = 0xFFFFFF;
+	var floatValueM = 12.2223;
+	var boolValueM = true;
+
+	function testMemberFields() {
+		var rtti = haxe.rtti.Rtti.getRtti(Issue3725);
+		var valueMap = new Map();
+		for (cf in rtti.fields) {
+			valueMap[cf.name] = cf.expr;
+		}
+		eq("'foo'", valueMap["stringValueM"]);
+		eq("12", valueMap["intValueM"]);
+		eq("0xFFFFFF", valueMap["intHexValueM"]);
+		eq("12.2223", valueMap["floatValueM"]);
+		eq("true", valueMap["boolValueM"]);
+	}
+}