Browse Source

[jvm] Deal with local function default arguments (#12094)

* deal with local function default arguments

* use on field functions too

* handle during JVM preprocessing

* Make analyzer thread-safe (#12083)

* remove shared lattice

* lose analyzer data dependency on com

* how bad could it be?

* make var ids atomic

* optimize because stuff fails otherwise

* cleanup

* how far can we take this?

* Revert "don't generate constructor on abstract classes"

This reverts commit d38ba0ea2a86b3942ba8b99cb2a71fc0359068da.

* consider _ prefix when checking reserved keywords (#12090)

closes #12089

* change parallel API

* [python] Mark threads as daemon threads. (#12096)

Otherwise background threads keep the program alive, which is not consistent with other targets.

* Revert "Make some filters thread-safe (#12087)"

This reverts commit 7ea09728860e9e005f2b54bc51ec3772fdfc3969.

# Conflicts:
#	src/filters/filters.ml

---------

Co-authored-by: Zeta <[email protected]>
Simon Krajewski 5 months ago
parent
commit
9af7aa3321
4 changed files with 102 additions and 203 deletions
  1. 0 159
      src/filters/defaultArguments.ml
  2. 0 13
      src/filters/filters.ml
  3. 84 31
      src/generators/genjvm.ml
  4. 18 0
      src/generators/genshared.ml

+ 0 - 159
src/filters/defaultArguments.ml

@@ -1,159 +0,0 @@
-(*
-	The Haxe Compiler
-	Copyright (C) 2005-2019  Haxe Foundation
-
-	This program is free software; you can redistribute it and/or
-	modify it under the terms of the GNU General Public License
-	as published by the Free Software Foundation; either version 2
-	of the License, or (at your option) any later version.
-
-	This program is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-	GNU General Public License for more details.
-
-	You should have received a copy of the GNU General Public License
-	along with this program; if not, write to the Free Software
-	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*)
-open Common
-open Type
-open Texpr.Builder
-
-(*
-	This Module Filter will go through all defined functions in all modules and change them
-	so they set all default arguments to be of a Nullable type, and adds the unroll from nullable to
-	the not-nullable type in the beginning of the function.
-*)
-
-let gen_check basic t nullable_var const pos =
-	let needs_cast t1 t2 =
-		let is_null t = match t with TAbstract ({a_path = ([],"Null")}, _) -> true | _ -> false in
-		(is_null t1) <> (is_null t2)
-	in
-
-	let const_t = const.etype in
-	let const = if needs_cast t const_t then mk_cast const t pos else const in
-
-	let arg = make_local nullable_var pos in
-	let arg = if needs_cast t nullable_var.v_type then mk_cast arg t pos else arg in
-
-	let check = binop Ast.OpEq (make_local nullable_var pos) (null nullable_var.v_type pos) basic.tbool pos in
-	mk (TIf (check, const, Some arg)) t pos
-
-let add_opt com block pos (var,opt) =
-	match opt with
-	| None | Some {eexpr = TConst TNull} ->
-		(var,opt)
-	| Some ({eexpr = TConst (TString str)} as e) ->
-		block := Texpr.set_default com.basic var e pos :: !block;
-		(var, opt)
-	| Some const ->
-		let basic = com.basic in
-		let nullable_var = alloc_var var.v_kind var.v_name (basic.tnull var.v_type) pos in
-		(* var v = (temp_var == null) ? const : cast temp_var; *)
-		let evar = mk (TVar(var, Some(gen_check basic var.v_type nullable_var const pos))) basic.tvoid pos in
-		block := evar :: !block;
-		(nullable_var, opt)
-
-let rec change_func com cl cf =
-	List.iter (change_func com cl) cf.cf_overloads;
-
-	match cf.cf_kind, follow cf.cf_type with
-	| _ when has_class_field_flag cf CfPostProcessed ->
-		()
-	| Var _, _ | Method MethDynamic, _ ->
-		()
-	| _, TFun(args, ret) ->
-		let is_ctor = cf.cf_name = "new" in
-		let basic = com.basic in
-
-		let found = ref false in
-
-		let args = ref (List.map (fun (n,opt,t) ->
-			(n,opt, if opt then (found := true; basic.tnull t) else t)
-		) args) in
-
-		(match !found, cf.cf_expr with
-		| true, Some ({ eexpr = TFunction tf } as texpr) ->
-			let block = ref [] in
-			let tf_args = List.map (add_opt com block tf.tf_expr.epos) tf.tf_args in
-			let arg_assoc = List.map2 (fun (v,o) (v2,_) -> v,(v2,o) ) tf.tf_args tf_args in
-			let rec extract_super e = match e.eexpr with
-				| TBlock (({ eexpr = TCall ({ eexpr = TConst TSuper }, _) } as e2) :: tl) ->
-					e2, tl
-				| TBlock (hd :: tl) ->
-					let e2, tl2 = extract_super hd in
-					e2, tl2 @ tl
-				| _ ->
-					raise Not_found
-			in
-			let block =
-				try
-					if not is_ctor then raise Not_found;
-
-					(* issue #2570 *)
-					(* check if the class really needs the super as the first statement -
-					just to make sure we don't inadvertently break any existing code *)
-					let rec check cl =
-						if not (Meta.has Meta.HxGen cl.cl_meta) then
-							()
-						else match cl.cl_super with
-							| None ->
-								raise Not_found
-							| Some (cl, _) ->
-								check cl
-					in
-					check cl;
-
-					let super, tl = extract_super tf.tf_expr in
-					(match super.eexpr with
-					| TCall ({ eexpr = TConst TSuper } as e1, args) ->
-						(* any super argument will be replaced by an inlined version of the check *)
-						let found = ref false in
-						let rec replace_args e =
-							match e.eexpr with
-							| TLocal v ->
-								(try
-									let v2,o = List.assq v arg_assoc in
-									let o = match o with
-									| None -> raise Not_found
-									| Some o -> o
-									in
-									found := true;
-									gen_check com.basic v.v_type v2 o e.epos
-								with Not_found -> e)
-							| _ ->
-								Type.map_expr replace_args e
-						in
-						let args = List.map replace_args args in
-						{ tf.tf_expr with eexpr = TBlock ((if !found then { super with eexpr = TCall (e1, args) } else super) :: !block @ tl) }
-					| _ -> Globals.die "" __LOC__)
-				with Not_found ->
-					Type.concat { tf.tf_expr with eexpr = TBlock !block; etype = basic.tvoid } tf.tf_expr
-			in
-
-			args := List.map (fun (v,s) -> (v.v_name, (s <> None), v.v_type)) tf_args;
-
-			let cf_type = TFun (!args, ret) in
-			cf.cf_expr <- Some { texpr with
-				eexpr = TFunction { tf with
-					tf_args = tf_args;
-					tf_expr = block
-				};
-				etype = cf_type
-			};
-			cf.cf_type <- cf_type
-
-		| _ -> ());
-		(if !found then cf.cf_type <- TFun(!args, ret))
-	| _, _ -> Globals.die "" __LOC__
-
-let run com md =
-	match md with
-	| TClassDecl cl ->
-		let apply = change_func com cl in
-		List.iter apply cl.cl_ordered_fields;
-		List.iter apply cl.cl_ordered_statics;
-		Option.may apply cl.cl_constructor;
-	| _ -> ()

+ 0 - 13
src/filters/filters.ml

@@ -665,19 +665,6 @@ let run tctx ectx main before_destruction =
 		"captured_vars",(fun _ -> CapturedVars.captured_vars com);
 	] in
 	List.iter (run_expression_filters tctx detail_times filters) new_types;
-	(* PASS 1.5: pre-analyzer type filters *)
-	let filters =
-		match com.platform with
-		| Jvm ->
-			[
-				DefaultArguments.run com;
-			]
-		| _ ->
-			[]
-	in
-	with_timer detail_times "type 1" None (fun () ->
-		List.iter (fun f -> List.iter f new_types) filters;
-	);
 	enter_stage com CAnalyzerStart;
 	if com.platform <> Cross then Analyzer.Run.run_on_types com new_types;
 	enter_stage com CAnalyzerDone;

+ 84 - 31
src/generators/genjvm.ml

@@ -516,6 +516,16 @@ let rvalue_any = RValue(None,None)
 let rvalue_sig jsig = RValue (Some jsig,None)
 let rvalue_type gctx t name = RValue (Some (jsignature_of_type gctx t),name)
 
+type local_ref = (int * (unit -> unit) * (unit -> unit))
+
+type transformed_arg = {
+	a_id : int;
+	a_name : string;
+	a_jsig_arg : jsignature;
+	a_jsig_local : jsignature option;
+	a_texpr : texpr option;
+}
+
 class texpr_to_jvm
 	(gctx : generation_context)
 	(field_info : field_generation_info option)
@@ -545,12 +555,14 @@ class texpr_to_jvm
 	method add_named_local (name : string) (jsig : jsignature) =
 		jm#add_local name jsig VarArgument
 
-	method add_local v init_state : (int * (unit -> unit) * (unit -> unit)) =
-		let t = self#vtype v.v_type in
-		let slot,load,store = jm#add_local v.v_name t init_state in
-		Hashtbl.add local_lookup v.v_id (slot,load,store);
+	method add_local2 id name jsig init_state =
+		let slot,load,store = jm#add_local name jsig init_state in
+		Hashtbl.add local_lookup id (slot,load,store);
 		slot,load,store
 
+	method add_local v init_state =
+		self#add_local2 v.v_id v.v_name (self#vtype v.v_type) init_state
+
 	method get_local_by_id (vid,vname) =
 		if vid = 0 && env = None then
 			(0,(fun () -> jm#load_this),(fun () -> die "" __LOC__))
@@ -600,6 +612,49 @@ class texpr_to_jvm
 		jm_init#construct ConstructInit jc_closure#get_this_path (fun () -> []);
 		jm_init#putstatic jc_closure#get_this_path jf_closure#get_name jf_closure#get_jsig;
 
+	method transform_arg (v : tvar) (eo : texpr option) =
+		let jsig_local = self#vtype v.v_type in
+		let dual_vars = eo <> None && is_unboxed jsig_local in
+		let jsig_arg = if dual_vars then get_boxed_type jsig_local else jsig_local in
+		{
+			a_id = v.v_id;
+			a_name = v.v_name;
+			a_jsig_arg = jsig_arg;
+			a_jsig_local = if dual_vars then Some jsig_local else None;
+			a_texpr = eo;
+		}
+
+	method handle_arg_inits (jm : JvmMethod.builder) (handler : texpr_to_jvm) (actual_args : local_ref list) (args : transformed_arg list) =
+		List.iter2 (fun (slot,load,store) arg -> match arg.a_texpr with
+			| Some e when (match e.eexpr with TConst TNull -> false | _ -> true) ->
+				begin match arg.a_jsig_local with
+					| Some jsig_local ->
+						load();
+						jm#if_then_else
+							(jm#get_code#if_nonnull arg.a_jsig_arg)
+							(fun () ->
+								handler#texpr (rvalue_sig jsig_local) e;
+							)
+							(fun () ->
+								load();
+								jm#cast jsig_local;
+							);
+						let _,_,store = handler#add_local2 arg.a_id arg.a_name jsig_local VarWillInit in
+						store();
+					| None ->
+						load();
+						jm#if_then
+							(jm#get_code#if_nonnull arg.a_jsig_arg)
+							(fun () ->
+								handler#texpr (rvalue_sig arg.a_jsig_arg) e;
+								jm#cast arg.a_jsig_arg;
+								store();
+							)
+				end
+			| _ ->
+				()
+		) actual_args args
+
 	method tfunction ret e tf =
 		let outside,accesses_this = Texpr.collect_captured_vars e in
 		let env = List.map (fun v ->
@@ -620,34 +675,17 @@ class texpr_to_jvm
 			| _ -> []
 		in
 		let args,ret =
-			let args = List.map (fun (v,eo) ->
-				(* TODO: Can we do this differently? *)
-				if eo <> None then v.v_type <- self#mknull v.v_type;
-				v.v_name,self#vtype v.v_type
-			) tf.tf_args in
+			let args = List.map (fun (v,eo) -> self#transform_arg v eo) tf.tf_args in
 			args,(return_of_type gctx tf.tf_type)
 		in
-		let jm_invoke = wf#generate_invoke args ret filter in
+		let jm_invoke = wf#generate_invoke (List.map (fun arg -> arg.a_name,arg.a_jsig_arg) args) ret filter in
 		let handler = new texpr_to_jvm gctx field_info jc_closure jm_invoke ret in
 		handler#set_env env;
-		let args = List.map (fun (v,eo) ->
-			handler#add_local v VarArgument,v,eo
-		) tf.tf_args in
+		let actual_args = List.map (fun arg ->
+			handler#add_local2 arg.a_id arg.a_name arg.a_jsig_arg VarArgument
+		) args in
 		jm_invoke#finalize_arguments;
-		List.iter (fun ((_,load,save),v,eo) -> match eo with
-			| Some e when (match e.eexpr with TConst TNull -> false | _ -> true) ->
-				load();
-				let jsig = self#vtype v.v_type in
-				jm_invoke#if_then
-					(jm_invoke#get_code#if_nonnull jsig)
-					(fun () ->
-						handler#texpr (rvalue_sig jsig) e;
-						jm_invoke#cast jsig;
-						save();
-					)
-			| _ ->
-				()
-		) args;
+		self#handle_arg_inits jm_invoke handler actual_args args;
 		handler#texpr RReturn tf.tf_expr;
 		begin match env with
 		| [] ->
@@ -2514,12 +2552,16 @@ class tclass_to_jvm gctx c = object(self)
 				e,[],None
 		in
 		let handler = new texpr_to_jvm gctx field_info jc jm tr in
-		List.iter (fun (v,_) ->
-			let slot,_,_ = handler#add_local v VarArgument in
+		let arg_pairs = List.map (fun (v,eo) ->
+			let arg = handler#transform_arg v eo in
+			let slot,load,store = handler#add_local2 arg.a_id arg.a_name arg.a_jsig_arg VarArgument in
 			let l = AnnotationHandler.convert_annotations v.v_meta in
 			List.iter (fun (path,annotation,is_runtime_visible) -> jm#add_argument_annotation slot path annotation is_runtime_visible) l;
-		) args;
+			(arg,(slot,load,store))
+		) args in
 		jm#finalize_arguments;
+		let args,actual_args = List.split arg_pairs in
+		handler#handle_arg_inits jm handler actual_args args;
 		begin match mtype with
 		| MConstructor ->
 			DynArray.iter (fun e ->
@@ -3054,13 +3096,24 @@ module Preprocessor = struct
 			) m.m_types
 		) gctx.gctx.modules;
 		(* preprocess classes *)
+		let patch_optional c =
+			let apply cf =
+				patch_optional gctx.gctx.basic cf;
+			in
+			List.iter apply c.cl_ordered_fields;
+			List.iter apply c.cl_ordered_statics;
+			Option.may apply c.cl_constructor;
+		in
 		List.iter (fun mt ->
 			match mt with
 			| TClassDecl c ->
 				if not (has_class_flag c CInterface) then
 					gctx.preprocessor#preprocess_class c
-				else if has_class_flag c CFunctionalInterface then
+				else begin
+					patch_optional c;
+					if has_class_flag c CFunctionalInterface then
 					check_functional_interface gctx c
+				end
 			| _ -> ()
 		) gctx.gctx.types;
 		(* find typedef-interface implementations *)

+ 18 - 0
src/generators/genshared.ml

@@ -52,6 +52,22 @@ module Info = struct
 	end
 end
 
+let rec patch_optional basic cf =
+	List.iter (patch_optional basic) cf.cf_overloads;
+	match follow cf.cf_type with
+		| TFun(args,ret) ->
+			let args = List.map (fun (n,o,t) ->
+				let o,t = if o && not (is_nullable t) then
+					(o,basic.tnull t)
+				else
+					(o,t)
+				in
+				(n,o,t)
+			) args in
+			cf.cf_type <- TFun(args,ret)
+		| _ ->
+			()
+
 open Info
 open OverloadResolution
 open Tanon_identification
@@ -170,6 +186,7 @@ object(self)
 		let has_dynamic_instance_method = ref false in
 		let has_field_init = ref false in
 		let field mtype cf =
+			patch_optional basic cf;
 			match mtype with
 			| MConstructor ->
 				()
@@ -199,6 +216,7 @@ object(self)
 				()
 			end;
 		| Some cf ->
+			patch_optional basic cf;
 			let field cf =
 				if !has_dynamic_instance_method then make_haxe cf;
 				begin match cf.cf_expr with