浏览代码

[js] use Array.prototype.slice.call(arguments, offset) for rest args and still generate non-rest args normally in ES5 (closes #9992)

Dan Korostelev 4 年之前
父节点
当前提交
0956a28e7f
共有 1 个文件被更改,包括 20 次插入32 次删除
  1. 20 32
      src/generators/genjs.ml

+ 20 - 32
src/generators/genjs.ml

@@ -336,31 +336,15 @@ let rec concat ctx s f = function
 	It's the only way to avoid disabling javascript VM optimizations of functions
 	with rest arguments.
 *)
-let declare_rest_args_legacy com args rest_arg =
-	let arguments = mk (TIdent "arguments") t_dynamic null_pos
-	and index i = mk (TConst (TInt (Int32.of_int i))) com.basic.tint null_pos in
-	let rec loop args i =
-		match args with
-		| [] -> []
-		| (v,e_opt) :: rest ->
-			(* var v = arguments[i]; *)
-			let value = mk (TArray (arguments,index i)) v.v_type null_pos in
-			let decl = mk (TVar (v,Some value)) com.basic.tvoid v.v_pos in
-
-			match e_opt with
-			| None -> decl :: loop rest (i + 1)
-			| Some e -> decl :: Texpr.set_default com.basic v e v.v_pos :: loop rest (i + 1)
-	in
-	let i = string_of_int (List.length args) in
-	let new_array = mk (TIdent ("new Array($l-"^ i ^")")) t_dynamic rest_arg.v_pos
-	and populate = mk (TIdent ("for(var $i=" ^ i ^ ";$i<$l;++$i){" ^ (ident rest_arg.v_name) ^ "[$i-" ^ i ^ "]=arguments[$i];}")) com.basic.tvoid rest_arg.v_pos
+let declare_rest_args_legacy com offset rest_arg =
+	let arguments = mk (TIdent "arguments") t_dynamic null_pos in
+	let array_slice = mk (TIdent ("Array.prototype.slice.call")) t_dynamic null_pos in
+	let array_slice_call = mk (TCall (
+		array_slice,
+		if offset = 0 then [arguments] else [arguments; Texpr.Builder.make_int com.basic offset null_pos]
+	)) rest_arg.v_type null_pos
 	in
-	loop args 0
-	@ [
-		mk (TIdent ("var $l=arguments.length")) com.basic.tvoid rest_arg.v_pos;
-		mk (TVar (rest_arg,Some new_array)) com.basic.tvoid rest_arg.v_pos;
-		populate
-	]
+	mk (TVar (rest_arg,Some array_slice_call)) com.basic.tvoid rest_arg.v_pos
 
 let fun_block ctx f p =
 	let e = List.fold_left (fun e (a,c) ->
@@ -902,6 +886,12 @@ and gen_function ?(keyword="function") ctx f pos =
 	let old = ctx.in_value, ctx.in_loop in
 	ctx.in_value <- None;
 	ctx.in_loop <- false;
+	let mk_non_rest_arg_names arg_list =
+		List.map (fun (v,_) ->
+			check_var_declaration v;
+			ident v.v_name
+		) arg_list
+	in
 	let f,args =
 		match List.rev f.tf_args with
 		| (v,None) :: args_rev when ExtType.is_rest (follow v.v_type) ->
@@ -914,21 +904,19 @@ and gen_function ?(keyword="function") ctx f pos =
 				) f.tf_args
 			(* Resort to `arguments` special object for ES < 6 *)
 			else
-				let args_decl = declare_rest_args_legacy ctx.com (List.rev args_rev) v in
+				let non_rest_args = List.rev args_rev in
+				let rest_args_decl = declare_rest_args_legacy ctx.com (List.length non_rest_args) v in
 				let body =
 					let el =
 						match f.tf_expr.eexpr with
-						| TBlock el -> args_decl @ el
-						| _ -> args_decl @ [f.tf_expr]
+						| TBlock el -> rest_args_decl :: el
+						| _ -> [rest_args_decl; f.tf_expr]
 					in
 					mk (TBlock el) f.tf_expr.etype f.tf_expr.epos
 				in
-				{ f with tf_args = []; tf_expr = body }, []
+				{ f with tf_args = non_rest_args; tf_expr = body }, mk_non_rest_arg_names non_rest_args
 		| _ ->
-			f, List.map (fun (v,_) ->
-				check_var_declaration v;
-				ident v.v_name
-			) f.tf_args
+			f, mk_non_rest_arg_names f.tf_args
 	in
 	print ctx "%s(%s) " keyword (String.concat "," args);
 	gen_expr ctx (fun_block ctx f pos);