浏览代码

[jvm] generate argument annotations

closes #10397
Simon Krajewski 3 年之前
父节点
当前提交
906bd0e016

+ 16 - 5
src/generators/genjvm.ml

@@ -211,7 +211,7 @@ let convert_fields gctx pfm =
 		l
 
 module AnnotationHandler = struct
-	let generate_annotations builder meta =
+	let convert_annotations meta =
 		let parse_path e =
 			let sl = try string_list_of_expr_path_raise e with Exit -> Error.error "Field expression expected" (pos e) in
 			let path = match sl with
@@ -248,17 +248,22 @@ module AnnotationHandler = struct
 			| _ ->
 				Error.error "Call expression expected" (pos e)
 		in
-		List.iter (fun (m,el,_) -> match m,el with
+		ExtList.List.filter_map (fun (m,el,_) -> match m,el with
 			| Meta.Meta,[e] ->
 				let path,annotation = parse_expr e in
 				let path = match path with
 					| [],name -> ["haxe";"root"],name
 					| _ -> path
 				in
-				builder#add_annotation path annotation;
+				Some(path,annotation)
 			| _ ->
-				()
+				None
 		) meta
+
+	let generate_annotations builder meta =
+		List.iter (fun (path,annotation) ->
+			builder#add_annotation path annotation
+		) (convert_annotations meta)
 end
 
 let enum_ctor_sig =
@@ -2360,7 +2365,13 @@ class tclass_to_jvm gctx c = object(self)
 		in
 		let handler = new texpr_to_jvm gctx jc jm tr in
 		List.iter (fun (v,_) ->
-			ignore(handler#add_local v VarArgument);
+			let slot,_,_ = handler#add_local v VarArgument in
+			let annot = AnnotationHandler.convert_annotations v.v_meta in
+			match annot with
+			| [] ->
+				()
+			| _ ->
+				jm#add_argument_annotation slot annot;
 		) args;
 		jm#finalize_arguments;
 		begin match mtype with

+ 5 - 0
src/generators/jvm/jvmAttribute.ml

@@ -85,6 +85,7 @@ type j_attribute =
 	| AttributeInnerClasses of jvm_inner_class array
 	| AttributeEnclosingMethod of jvm_constant_pool_index * jvm_constant_pool_index
 	| AttributeRuntimeVisibleAnnotations of j_annotation array
+	| AttributeRuntimeVisibleParameterAnnotations of j_annotation array array
 	| AttributeBootstrapMethods of j_bootstrap_method array
 
 let write_verification_type ch = function
@@ -235,6 +236,10 @@ let write_attribute pool jvma =
 	| AttributeRuntimeVisibleAnnotations al ->
 		write_array16 ch write_annotation al;
 		"RuntimeVisibleAnnotations"
+	| AttributeRuntimeVisibleParameterAnnotations al ->
+		write_byte ch (Array.length al);
+		Array.iter (write_array16 ch write_annotation) al;
+		"RuntimeVisibleParameterAnnotations"
 	| AttributeBootstrapMethods a ->
 		write_array16 ch (fun _ bm ->
 			write_ui16 ch bm.bm_method_ref;

+ 30 - 27
src/generators/jvm/jvmBuilder.ml

@@ -36,6 +36,35 @@ type export_config = {
 	export_debug : bool;
 }
 
+let convert_annotations pool annotations =
+	let a = Array.map (fun (jsig,l) ->
+		let offset = pool#add_string (generate_signature false jsig) in
+		let l = List.map (fun (name,ak) ->
+			let offset = pool#add_string name in
+			let rec loop ak = match ak with
+				| AInt i32 ->
+					'I',ValConst(pool#add (ConstInt i32))
+				| ADouble f ->
+					'D',ValConst(pool#add (ConstDouble f))
+				| AString s ->
+					's',ValConst(pool#add_string s)
+				| ABool b ->
+					'Z',ValConst(pool#add (ConstInt (if b then Int32.one else Int32.zero)))
+				| AEnum(jsig,name) ->
+					'e',ValEnum(pool#add_string (generate_signature false jsig),pool#add_string name)
+				| AArray l ->
+					let l = List.map (fun ak -> loop ak) l in
+					'[',ValArray(Array.of_list l)
+			in
+			offset,loop ak
+		) l in
+		{
+			ann_type = offset;
+			ann_elements = Array.of_list l;
+		}
+	) annotations in
+	a
+
 class base_builder = object(self)
 	val mutable access_flags = 0
 	val attributes = DynArray.create ()
@@ -54,33 +83,7 @@ class base_builder = object(self)
 	method private commit_annotations pool =
 		if DynArray.length annotations > 0 then begin
 			let open JvmAttribute in
-			let a = DynArray.to_array annotations in
-			let a = Array.map (fun (jsig,l) ->
-				let offset = pool#add_string (generate_signature false jsig) in
-				let l = List.map (fun (name,ak) ->
-					let offset = pool#add_string name in
-					let rec loop ak = match ak with
-						| AInt i32 ->
-							'I',ValConst(pool#add (ConstInt i32))
-						| ADouble f ->
-							'D',ValConst(pool#add (ConstDouble f))
-						| AString s ->
-							's',ValConst(pool#add_string s)
-						| ABool b ->
-							'Z',ValConst(pool#add (ConstInt (if b then Int32.one else Int32.zero)))
-						| AEnum(jsig,name) ->
-							'e',ValEnum(pool#add_string (generate_signature false jsig),pool#add_string name)
-						| AArray l ->
-							let l = List.map (fun ak -> loop ak) l in
-							'[',ValArray(Array.of_list l)
-					in
-					offset,loop ak
-				) l in
-				{
-					ann_type = offset;
-					ann_elements = Array.of_list l;
-				}
-			) a in
+			let a = convert_annotations pool (DynArray.to_array annotations) in
 			self#add_attribute (AttributeRuntimeVisibleAnnotations a)
 		end
 

+ 17 - 0
src/generators/jvm/jvmMethod.ml

@@ -141,6 +141,7 @@ class builder jc name jsig = object(self)
 	val mutable stack_frames = []
 	val mutable exceptions = []
 	val mutable argument_locals = []
+	val mutable argument_annotations = Hashtbl.create 0
 	val mutable thrown_exceptions = Hashtbl.create 0
 	val mutable closure_count = 0
 	val mutable regex_count = 0
@@ -1002,6 +1003,10 @@ class builder jc name jsig = object(self)
 	method replace_top jsig =
 		code#get_stack#replace jsig
 
+	method add_argument_annotation (slot : int) (a : (path * annotation) list) =
+		let a = Array.of_list (List.map (fun (path,annot) -> TObject(path,[]),annot) a) in
+		Hashtbl.add argument_annotations slot a
+
 	(** This function has to be called once all arguments are declared. *)
 	method finalize_arguments =
 		argument_locals <- locals
@@ -1123,6 +1128,18 @@ class builder jc name jsig = object(self)
 		end;
 		if Hashtbl.length thrown_exceptions > 0 then
 			self#add_attribute (AttributeExceptions (Array.of_list (Hashtbl.fold (fun k _ c -> k :: c) thrown_exceptions [])));
+		if Hashtbl.length argument_annotations > 0 then begin
+			let l = List.length argument_locals in
+			let offset = if self#has_method_flag MStatic then 0 else 1 in
+			let a = Array.init (l - offset) (fun i ->
+				try
+					let annot = Hashtbl.find argument_annotations (i + offset) in
+					convert_annotations jc#get_pool annot
+				with Not_found ->
+					[||]
+			) in
+			DynArray.add attributes (AttributeRuntimeVisibleParameterAnnotations a)
+		end;
 		let attributes = self#export_attributes jc#get_pool in
 		let offset_name = jc#get_pool#add_string name in
 		let jsig = generate_method_signature false jsig in

+ 159 - 0
tests/unit/src/unit/issues/Issue10397.hx

@@ -0,0 +1,159 @@
+package unit.issues;
+
+import utest.Assert.*;
+
+#if jvm
+@:meta(java.lang.annotation.Documented())
+private class NotMain {
+	static public function gather() {
+		final main = new NotMain();
+
+		final cls = java.Lib.getNativeType(main);
+
+		final classAnnotations = [];
+		final methods = new Map();
+		final fieldAnnotations = [];
+
+		for (an in cls.getAnnotations())
+			classAnnotations.push(an.toString());
+
+		for (f in cls.getMethods()) {
+			final methodAnnotations = [];
+			final methodArgAnnotations = new Map();
+			for (an in f.getAnnotations())
+				methodAnnotations.push(an.toString());
+
+			for (arg in f.getParameters()) {
+				var annots = [];
+				for (an in arg.getAnnotations()) {
+					annots.push(an.toString());
+				}
+				methodArgAnnotations[arg.getName()] = annots;
+			}
+			methods[f.getName()] = {methodAnnotations: methodAnnotations, methodArgAnnotations: methodArgAnnotations};
+		}
+
+		for (f in cls.getConstructors()) {
+			final methodAnnotations = [];
+			final methodArgAnnotations = new Map();
+			for (an in f.getAnnotations())
+				methodAnnotations.push(an.toString());
+
+			for (arg in f.getParameters()) {
+				var annots = [];
+				for (an in arg.getAnnotations()) {
+					annots.push(an.toString());
+				}
+				methodArgAnnotations[arg.getName()] = annots;
+			}
+			methods[f.toString()] = {methodAnnotations: methodAnnotations, methodArgAnnotations: methodArgAnnotations};
+		}
+
+		for (f in cls.getFields())
+			if (f.getName() == 'member') {
+				for (an in f.getAnnotations())
+					fieldAnnotations.push(an.toString());
+			}
+
+		return {
+			classAnnotations: classAnnotations,
+			methods: methods,
+			fieldAnnotations: fieldAnnotations
+		}
+	}
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep
+	public overload function new(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String) {}
+
+	overload function new() {}
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep var member:String;
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep
+	public function on0(@:meta(java.lang.annotation.Documented()) arg0:String):String {
+		return 'foo';
+	}
+
+	@:keep
+	public function on1(arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String {
+		return 'foo';
+	}
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep
+	public function onBoth(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String {
+		return 'foo';
+	}
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep
+	public function on0Static(@:meta(java.lang.annotation.Documented()) arg0:String):String {
+		return 'foo';
+	}
+
+	@:keep
+	public function on1Static(arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String {
+		return 'foo';
+	}
+
+	@:meta(java.lang.annotation.Documented())
+	@:keep
+	public function onBothStatic(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String {
+		return 'foo';
+	}
+}
+#end
+
+class Issue10397 extends unit.Test {
+	#if jvm
+	function test() {
+		var annots = NotMain.gather();
+		same([
+			"@java.lang.annotation.Documented()",
+			"@haxe.jvm.annotation.ClassReflectionInformation(hasSuperClass=false)"
+		], annots.classAnnotations);
+		same({
+			methodAnnotations: ["@java.lang.annotation.Documented()"],
+			methodArgAnnotations: ["arg0" => ["@java.lang.annotation.Documented()"]],
+		}, annots.methods["on0"]);
+		same({
+			methodAnnotations: [],
+			methodArgAnnotations: ["arg0" => [], "arg1" => ["@java.lang.annotation.Documented()"]],
+		}, annots.methods["on1"]);
+		same({
+			methodAnnotations: ["@java.lang.annotation.Documented()"],
+			methodArgAnnotations: [
+				"arg0" => ["@java.lang.annotation.Documented()"],
+				"arg1" => ["@java.lang.annotation.Documented()"]
+			],
+		}, annots.methods["onBoth"]);
+		same({
+			methodAnnotations: ["@java.lang.annotation.Documented()"],
+			methodArgAnnotations: ["arg0" => ["@java.lang.annotation.Documented()"]],
+		}, annots.methods["on0Static"]);
+		same({
+			methodAnnotations: [],
+			methodArgAnnotations: ["arg0" => [], "arg1" => ["@java.lang.annotation.Documented()"]],
+		}, annots.methods["on1Static"]);
+		same({
+			methodAnnotations: ["@java.lang.annotation.Documented()"],
+			methodArgAnnotations: [
+				"arg0" => ["@java.lang.annotation.Documented()"],
+				"arg1" => ["@java.lang.annotation.Documented()"]
+			],
+		}, annots.methods["onBothStatic"]);
+		same({
+			methodAnnotations: ["@java.lang.annotation.Documented()"],
+			methodArgAnnotations: [
+				"arg0" => ["@java.lang.annotation.Documented()"],
+				"arg1" => ["@java.lang.annotation.Documented()"]
+			],
+		},
+			annots.methods["public unit.issues.Issue10397$NotMain(java.lang.String,java.lang.String)"]);
+		same(["@java.lang.annotation.Documented()"], annots.fieldAnnotations);
+	}
+	#end
+}