Răsfoiți Sursa

[jvm] make functional interface detection more accurate

see #11054
Simon Krajewski 1 an în urmă
părinte
comite
c1ed8dc818
2 a modificat fișierele cu 111 adăugiri și 16 ștergeri
  1. 49 16
      src/generators/jvm/jvmFunctions.ml
  2. 62 0
      tests/unit/src/unit/issues/Issue11054.hx

+ 49 - 16
src/generators/jvm/jvmFunctions.ml

@@ -294,6 +294,14 @@ module JavaFunctionalInterfaces = struct
 		jparams : string list;
 	}
 
+	let string_of_functional_interface jfi = TPrinting.Printer.s_record_fields "" [
+		"jargs",String.concat ", " (List.map (generate_signature true) jfi.jargs);
+		"jret",Option.map_default (generate_signature true) "None" jfi.jret;
+		"jpath",Globals.s_type_path jfi.jpath;
+		"jname",jfi.jname;
+		"jparams",String.concat ", " jfi.jparams;
+	]
+
 	let java_functional_interfaces = DynArray.create ()
 
 	let add args ret path name params =
@@ -307,30 +315,55 @@ module JavaFunctionalInterfaces = struct
 		DynArray.add java_functional_interfaces jfi
 
 	let unify jfi args ret =
-		let rec loop params want have = match want,have with
+		let params = ref [] in
+		let rec unify jsig1 jsig2 = match jsig1,jsig2 with
+			| TObject(path1,params1),TObject(path2,params2) ->
+				path1 = path2 &&
+				unify_params params1 params2
+			| TTypeParameter n,jsig
+			| jsig,TTypeParameter n ->
+				List.mem_assoc n !params || begin
+					params := (n,jsig) :: !params;
+					true
+				end
+			| _ ->
+				jsig1 = jsig2
+		and unify_params params1 params2 = match params1,params2 with
+			| [],_
+			| _,[] ->
+				(* Assume raw type, I guess? *)
+				true
+			| param1 :: params1,param2 :: params2 ->
+				match param1,param2 with
+				| TAny,_
+				| _,TAny ->
+					(* Is this correct in both directions? *)
+					unify_params params1 params2
+				| TType(_,jsig1),TType(_,jsig2) ->
+					(* TODO: wildcard? *)
+					unify jsig1 jsig2 && unify_params params1 params2
+		in
+		let rec loop want have = match want,have with
 			| [],[] ->
-				Some (jfi,List.map (fun s -> TType(WNone,List.assoc s params)) jfi.jparams)
+				let params = List.map (fun s ->
+					try
+						TType(WNone,List.assoc s !params)
+					with Not_found ->
+						TAny
+				) jfi.jparams in
+				Some (jfi,params)
 			| want1 :: want,have1 :: have ->
-				begin match want1 with
-				| TTypeParameter n ->
-					let have1 = get_boxed_type have1 in
-					loop ((n,have1) :: params) want have
-				| _ ->
-					if have1 <> want1 then None
-					else loop params want have
-				end
+				if unify have1 want1 then loop want have
+				else None
 			| _ ->
 				None
 		in
 		match jfi.jret,ret with
 		| None,None ->
-			loop [] jfi.jargs args
-		| Some (TTypeParameter n),Some jsig ->
-			let jsig = get_boxed_type jsig in
-			loop [n,jsig] jfi.jargs args
+			loop jfi.jargs args
 		| Some jsig1,Some jsig2 ->
-			if jsig1 <> jsig2 then None
-			else loop [] jfi.jargs args
+			if unify jsig1 jsig2 then loop jfi.jargs args
+			else None
 		| _ ->
 			None
 

+ 62 - 0
tests/unit/src/unit/issues/Issue11054.hx

@@ -0,0 +1,62 @@
+package unit.issues;
+
+private abstract class Robot<T> {
+	public function new() {}
+
+	public function performTask(listener:T) {}
+
+	public function toString() {
+		return "Robot";
+	}
+}
+
+private interface IGreetRobot {
+	function greet<T>(robot:Robot<T>):Void;
+}
+
+private interface IMathOperation {
+	function operate(a:Int, b:Int):Int;
+}
+
+private class MathRobot extends Robot<IMathOperation> {
+	override function performTask(listener:IMathOperation) {
+		super.performTask(listener);
+		var result = listener.operate(3, 4);
+	}
+}
+
+private class GreetRobot extends Robot<IGreetRobot> {
+	var target:Robot<Dynamic>;
+
+	public function new(target:Robot<Dynamic>) {
+		super();
+		this.target = target;
+	}
+
+	override function performTask(listener:IGreetRobot) {
+		super.performTask(listener);
+		listener.greet(target);
+	}
+}
+
+class Issue11054 extends Test {
+	function test() {
+		var robot1 = new MathRobot();
+		var robot2 = new GreetRobot(robot1);
+
+		robot1.performTask(add);
+		robot1.performTask(function(a:Int, b:Int):Int {
+			return a - b;
+		});
+
+		var called = false;
+		robot2.performTask(function(target) {
+			called = true;
+		});
+		t(called);
+	}
+
+	static function add(a:Int, b:Int):Int {
+		return a + b;
+	}
+}