2
0
Эх сурвалжийг харах

Variable / Rest arguments (#9961)

* js, php, lua, python

* spread operator

* extract iterators into separate modules in haxe.iterators

* more tests

* better error msg

* [php] update for additional API

* [js] additional API; spread operator support

* removed @:coreType from haxe.Rest

* [inliner] support inlining of functions with rest arguments

* [php] optimize array decl if immediately converted to native array

* [inliner] fix rest args inlining

* [php] don't optimize ArrayDecl -> NativeArray if passed to php.Ref<T>

* more tests

* [python] support spread operator and additional API

* added a warning to the doc of `haxe.Rest.of(array)`

* [lua] update to the new API; support spread operator; fixed `haxe.Rest` as a return type

* [jvm] wip

* [eval] implementation

* Allow `...a:Int` in arg declaration instead of `a:haxe.Rest<Int>`

* and in function types

* [tests] also test static/instance closures with rest args.
Some  targets have quirks with that.

* [cs] wip

* [cs] support static/instance closures

* [cs] finally tamed ctors & overloading

* [jvm] finished

* [java] wip

* [java] done

* [neko] Array-based implementation

* [flash] based on a giant `if(rest.length == N)...` switch

* [flash] keep `function.apply(this, [arg1, arg2].concat(rest))` approach for non-`new` calls  for unlimited rest arguments amount with spread operator

* more tests

* test inlining with spread op

* [hl] implementation on top of `Array<T>`

* added pf_supports_rest_args; removed specific implementations from hl, neko, eval

* [cpp] Array-based implementation for haxe-generated code; cpp.Rest<T> for externs

* workaround cppia issue

* cleanup
Aleksandr Kuzmenko 4 жил өмнө
parent
commit
683b22440e
50 өөрчлөгдсөн 1135 нэмэгдсэн , 127 устгасан
  1. 52 15
      src/codegen/gencommon/castDetect.ml
  2. 1 2
      src/codegen/gencommon/closuresToClass.ml
  3. 36 0
      src/codegen/gencommon/gencommon.ml
  4. 3 1
      src/codegen/gencommon/normalize.ml
  5. 1 1
      src/codegen/swfLoader.ml
  6. 10 0
      src/context/common.ml
  7. 5 5
      src/core/ast.ml
  8. 1 0
      src/core/json/genjson.ml
  9. 5 0
      src/core/tOther.ml
  10. 3 1
      src/generators/gencpp.ml
  11. 5 1
      src/generators/gencs.ml
  12. 2 0
      src/generators/genhl.ml
  13. 31 6
      src/generators/genjava.ml
  14. 128 18
      src/generators/genjs.ml
  15. 11 3
      src/generators/genjvm.ml
  16. 48 18
      src/generators/genlua.ml
  17. 2 1
      src/generators/genneko.ml
  18. 57 4
      src/generators/genphp7.ml
  19. 2 1
      src/generators/genpy.ml
  20. 96 13
      src/generators/genswf9.ml
  21. 1 1
      src/macro/eval/evalDebugMisc.ml
  22. 4 0
      src/macro/eval/evalJit.ml
  23. 2 0
      src/macro/macroApi.ml
  24. 36 0
      src/optimization/inline.ml
  25. 13 1
      src/syntax/grammar.mly
  26. 1 1
      src/syntax/lexer.ml
  27. 1 0
      src/syntax/reification.ml
  28. 44 3
      src/typing/callUnification.ml
  29. 5 8
      src/typing/functionArguments.ml
  30. 7 0
      src/typing/operators.ml
  31. 13 10
      src/typing/overloadResolution.ml
  32. 25 0
      std/cpp/Rest.hx
  33. 0 2
      std/cpp/Stdio.hx
  34. 50 0
      std/cs/_std/haxe/Rest.hx
  35. 70 0
      std/haxe/Rest.hx
  36. 4 4
      std/haxe/extern/Rest.hx
  37. 19 0
      std/haxe/iterators/RestIterator.hx
  38. 19 0
      std/haxe/iterators/RestKeyValueIterator.hx
  39. 5 0
      std/haxe/macro/Expr.hx
  40. 1 0
      std/haxe/macro/Printer.hx
  41. 60 0
      std/java/_std/haxe/Rest.hx
  42. 51 0
      std/lua/_std/haxe/Rest.hx
  43. 1 1
      std/php/Syntax.hx
  44. 46 0
      std/php/_std/haxe/Rest.hx
  45. 0 4
      tests/misc/projects/Issue3238/NonExtern.hx
  46. 0 1
      tests/misc/projects/Issue3238/non-extern-fail.hxml
  47. 0 1
      tests/misc/projects/Issue3238/non-extern-fail.hxml.stderr
  48. 1 0
      tests/unit/src/unit/TestMain.hx
  49. 1 0
      tests/unit/src/unit/TestMatch.hx
  50. 156 0
      tests/unit/src/unit/TestRest.hx

+ 52 - 15
src/codegen/gencommon/castDetect.ml

@@ -543,8 +543,11 @@ let select_overload gen applied_f overloads types params =
 	let rec check_arg arglist elist =
 	let rec check_arg arglist elist =
 		match arglist, elist with
 		match arglist, elist with
 			| [], [] -> true (* it is valid *)
 			| [], [] -> true (* it is valid *)
-			| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") }, [t])) :: [], elist ->
+			| (_,_,t) :: [], elist when ExtType.is_rest t ->
+				(match follow t with
+				| TAbstract({ a_path = (["haxe"],"Rest") }, [t]) ->
 				List.for_all (fun (_,_,et) -> Type.type_iseq (clean_t et) (clean_t t)) elist
 				List.for_all (fun (_,_,et) -> Type.type_iseq (clean_t et) (clean_t t)) elist
+				| _ -> die "" __LOC__)
 			| (_,_,t) :: arglist, (_,_,et) :: elist when Type.type_iseq (clean_t et) (clean_t t) ->
 			| (_,_,t) :: arglist, (_,_,et) :: elist when Type.type_iseq (clean_t et) (clean_t t) ->
 				check_arg arglist elist
 				check_arg arglist elist
 			| _ -> false
 			| _ -> false
@@ -615,19 +618,39 @@ let choose_ctor gen cl tparams etl maybe_empty_t p =
 			ret, !count > 1
 			ret, !count > 1
 		| _ ->
 		| _ ->
 			let len = List.length etl in
 			let len = List.length etl in
-			let ret = List.filter (fun cf -> List.length (fst (get_fun cf.cf_type)) = len) ctors in
+			let ret = List.filter (fun cf -> List.length (fst (get_fun cf.cf_type)) <= len) ctors in
 			ret, (match ret with | _ :: [] -> false | _ -> true)
 			ret, (match ret with | _ :: [] -> false | _ -> true)
 	in
 	in
 	let rec check_arg arglist elist =
 	let rec check_arg arglist elist =
 		match arglist, elist with
 		match arglist, elist with
 		| [], [] -> true
 		| [], [] -> true
-		| (_,_,t) :: arglist, et :: elist -> (try
-			let t = run_follow gen t in
-			unify et t;
-			check_arg arglist elist
-		with | Unify_error el ->
-			(* List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) p) el; *)
-			false)
+		| [(_,_,t)], elist when ExtType.is_rest (follow t) ->
+			let is_rest_array arg_t =
+				Type.fast_eq (Abstract.follow_with_abstracts t) (Abstract.follow_with_abstracts arg_t)
+			in
+			(match elist with
+			| [arg_t] when is_rest_array arg_t -> true
+			| _ ->
+				match follow t with
+				| TAbstract ({ a_path = ["haxe"],"Rest" }, [t1]) ->
+					let t1 = run_follow gen t1 in
+					(try
+						List.iter (fun et -> unify et t1) elist;
+						true
+					with Unify_error _ ->
+						false
+					)
+				| _ -> die "" __LOC__
+			)
+		| (_,_,t) :: arglist, et :: elist ->
+			(try
+				let t = run_follow gen t in
+				unify et t;
+				check_arg arglist elist
+			with Unify_error el ->
+				(* List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) p) el; *)
+				false
+			)
 		| _ ->
 		| _ ->
 			false
 			false
 	in
 	in
@@ -645,8 +668,22 @@ let choose_ctor gen cl tparams etl maybe_empty_t p =
 
 
 let change_rest tfun elist =
 let change_rest tfun elist =
 	let rec loop acc arglist elist = match arglist, elist with
 	let rec loop acc arglist elist = match arglist, elist with
-		| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") },[t])) :: [], elist ->
-			List.rev (List.map (fun _ -> "rest",false,t) elist @ acc)
+		| (_,_,t) as arg :: [], elist when ExtType.is_rest t ->
+			(match elist with
+			| [{ eexpr = TUnop (Spread,Prefix,e) }] ->
+				List.rev (arg :: acc)
+			| _ ->
+				(match follow t with
+				| TAbstract({ a_path = (["haxe"],"Rest") },[t1]) ->
+					let is_rest_array e =
+						Type.fast_eq (Abstract.follow_with_abstracts t) (Abstract.follow_with_abstracts e.etype)
+					in
+					(match elist with
+					| [e] when is_rest_array e -> List.rev (("rest",false,t) :: acc)
+					| _ -> List.rev (List.map (fun _ -> "rest",false,t1) elist @ acc)
+					)
+				| _ -> die "" __LOC__)
+			)
 		| (n,o,t) :: arglist, _ :: elist ->
 		| (n,o,t) :: arglist, _ :: elist ->
 			loop ((n,o,t) :: acc) arglist elist
 			loop ((n,o,t) :: acc) arglist elist
 		| _, _ ->
 		| _, _ ->
@@ -1118,10 +1155,10 @@ let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_paramete
 							handle e t1 t2
 							handle e t1 t2
 					in
 					in
 					let stl = gen.greal_type_param (TClassDecl sup) stl in
 					let stl = gen.greal_type_param (TClassDecl sup) stl in
-					let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
+					let args,rt = get_fun (apply_params sup.cl_params stl cf.cf_type) in
 					let eparams = List.map2 (fun e (_,_,t) ->
 					let eparams = List.map2 (fun e (_,_,t) ->
 						handle (run e) t e.etype
 						handle (run e) t e.etype
-					) eparams args in
+					) (wrap_rest_args gen (TFun (args,rt)) eparams e.epos) args in
 					{ e with eexpr = TCall(ef, eparams) }
 					{ e with eexpr = TCall(ef, eparams) }
 				with | Not_found ->
 				with | Not_found ->
 					gen.gcon.warning "No overload found for this constructor call" e.epos;
 					gen.gcon.warning "No overload found for this constructor call" e.epos;
@@ -1146,10 +1183,10 @@ let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_paramete
 						handle e t1 t2
 						handle e t1 t2
 				in
 				in
 				let stl = gen.greal_type_param (TClassDecl sup) stl in
 				let stl = gen.greal_type_param (TClassDecl sup) stl in
-				let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
+				let args,rt = get_fun (apply_params sup.cl_params stl cf.cf_type) in
 				let eparams = List.map2 (fun e (_,_,t) ->
 				let eparams = List.map2 (fun e (_,_,t) ->
 					handle (run e) t e.etype
 					handle (run e) t e.etype
-				) eparams args in
+				) (wrap_rest_args gen (TFun (args,rt)) eparams e.epos) args in
 				{ e with eexpr = TNew(cl, tparams, eparams) }
 				{ e with eexpr = TNew(cl, tparams, eparams) }
 			with | Not_found ->
 			with | Not_found ->
 				gen.gcon.warning "No overload found for this constructor call" e.epos;
 				gen.gcon.warning "No overload found for this constructor call" e.epos;

+ 1 - 2
src/codegen/gencommon/closuresToClass.ml

@@ -653,7 +653,6 @@ let configure gen ft =
 	in
 	in
 	gen.gexpr_filters#add name (PCustom priority) run
 	gen.gexpr_filters#add name (PCustom priority) run
 
 
-
 (*
 (*
 	this submodule will provide the default implementation for the C# and Java targets.
 	this submodule will provide the default implementation for the C# and Java targets.
 
 
@@ -853,7 +852,7 @@ struct
 
 
 		let dynamic_fun_call call_expr =
 		let dynamic_fun_call call_expr =
 			let tc, params = match call_expr.eexpr with
 			let tc, params = match call_expr.eexpr with
-				| TCall(tc, params) -> tc, params
+				| TCall(tc, params) -> tc,wrap_rest_args gen tc.etype params tc.epos
 				| _ -> die "" __LOC__
 				| _ -> die "" __LOC__
 			in
 			in
 			let ct = gen.greal_type call_expr.etype in
 			let ct = gen.greal_type call_expr.etype in

+ 36 - 0
src/codegen/gencommon/gencommon.ml

@@ -996,6 +996,42 @@ let get_real_fun gen t =
 let mk_nativearray_decl gen t el pos =
 let mk_nativearray_decl gen t el pos =
 	mk (TCall (mk (TIdent "__array__") t_dynamic pos, el)) (gen.gclasses.nativearray t) pos
 	mk (TCall (mk (TIdent "__array__") t_dynamic pos, el)) (gen.gclasses.nativearray t) pos
 
 
+
+(**
+	Wraps rest arguments into a native array.
+	E.g. transforms params from `callee(param, rest1, rest2, ..., restN)` into
+	`callee(param, untyped __array__(rest1, rest2, ..., restN))`
+*)
+let wrap_rest_args gen callee_type params p =
+	match follow callee_type with
+	| TFun(args, _) ->
+		let rec loop args params =
+			match args, params with
+			(* last argument expects rest parameters *)
+			| [(_,_,t)], params when ExtType.is_rest (follow t) ->
+				(match params with
+				(* In case of `...rest` just use `rest` *)
+				| [{ eexpr = TUnop(Spread,Prefix,e) }] -> [e]
+				(* In other cases: `untyped __array__(param1, param2, ...)` *)
+				| _ ->
+					match Abstract.follow_with_abstracts t with
+					| TInst ({ cl_path = _,"NativeArray" }, [t1]) ->
+						let pos = punion_el (List.map (fun e -> ((),e.epos)) params) in
+						let t1 = if Common.defined gen.gcon Define.EraseGenerics then t_dynamic else t1 in
+						[mk_nativearray_decl gen t1 params pos]
+					| _ ->
+						die ~p "Unexpected rest arguments type" __LOC__
+				)
+			| a :: args, e :: params ->
+				e :: loop args params
+			| [], params ->
+				params
+			| _ :: _, [] ->
+				[]
+		in
+		loop args params
+	| _ -> params
+
 let ensure_local com block name e =
 let ensure_local com block name e =
 	match e.eexpr with
 	match e.eexpr with
 	| TLocal _ -> e
 	| TLocal _ -> e

+ 3 - 1
src/codegen/gencommon/normalize.ml

@@ -39,13 +39,15 @@ let rec filter_param (stack:t list) t =
 		| Some t -> filter_param stack t)
 		| Some t -> filter_param stack t)
 	| TInst(_,[]) | TEnum(_,[]) | TAbstract(_,[]) ->
 	| TInst(_,[]) | TEnum(_,[]) | TAbstract(_,[]) ->
 		t
 		t
+	| TType({ t_path = (["haxe";"extern"],"Rest") },_) ->
+		filter_param stack (follow t)
 	| TType(td,tl) ->
 	| TType(td,tl) ->
 		TType(td,List.map (filter_param stack) tl)
 		TType(td,List.map (filter_param stack) tl)
 	| TInst(c,tl) ->
 	| TInst(c,tl) ->
 		TInst(c,List.map (filter_param stack) tl)
 		TInst(c,List.map (filter_param stack) tl)
 	| TEnum(e,tl) ->
 	| TEnum(e,tl) ->
 		TEnum(e,List.map (filter_param stack) tl)
 		TEnum(e,List.map (filter_param stack) tl)
-	| TAbstract({ a_path = (["haxe";"extern"],"Rest") } as a,tl) ->
+	| TAbstract({ a_path = (["haxe"],"Rest") } as a,tl) ->
 		TAbstract(a, List.map (filter_param stack) tl)
 		TAbstract(a, List.map (filter_param stack) tl)
 	| TAbstract({a_path = [],"Null"} as a,[t]) ->
 	| TAbstract({a_path = [],"Null"} as a,[t]) ->
 		TAbstract(a,[filter_param stack t])
 		TAbstract(a,[filter_param stack t])

+ 1 - 1
src/codegen/swfLoader.ml

@@ -47,7 +47,7 @@ let tp_dyn = { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; }
 let ct_dyn = CTPath tp_dyn
 let ct_dyn = CTPath tp_dyn
 
 
 let ct_rest = CTPath {
 let ct_rest = CTPath {
-	tpackage = ["haxe"; "extern"];
+	tpackage = ["haxe"];
 	tname = "Rest";
 	tname = "Rest";
 	tparams = [TPType (ct_dyn,null_pos)];
 	tparams = [TPType (ct_dyn,null_pos)];
 	tsub = None;
 	tsub = None;

+ 10 - 0
src/context/common.ml

@@ -178,6 +178,8 @@ type platform_config = {
 	pf_supports_threads : bool;
 	pf_supports_threads : bool;
 	(** target supports Unicode **)
 	(** target supports Unicode **)
 	pf_supports_unicode : bool;
 	pf_supports_unicode : bool;
+	(** target supports rest arguments **)
+	pf_supports_rest_args : bool;
 	(** exceptions handling config **)
 	(** exceptions handling config **)
 	pf_exceptions : exceptions_config;
 	pf_exceptions : exceptions_config;
 	(** the scoping of local variables *)
 	(** the scoping of local variables *)
@@ -411,6 +413,7 @@ let default_config =
 		pf_this_before_super = true;
 		pf_this_before_super = true;
 		pf_supports_threads = false;
 		pf_supports_threads = false;
 		pf_supports_unicode = true;
 		pf_supports_unicode = true;
+		pf_supports_rest_args = false;
 		pf_exceptions = {
 		pf_exceptions = {
 			ec_native_throws = [];
 			ec_native_throws = [];
 			ec_native_catches = [];
 			ec_native_catches = [];
@@ -438,6 +441,7 @@ let get_config com =
 			pf_capture_policy = if es6 then CPNone else CPLoopVars;
 			pf_capture_policy = if es6 then CPNone else CPLoopVars;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_this_before_super = not es6; (* cannot access `this` before `super()` when generating ES6 classes *)
 			pf_this_before_super = not es6; (* cannot access `this` before `super()` when generating ES6 classes *)
+			pf_supports_rest_args = true;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_native_throws = [
 				ec_native_throws = [
 					["js";"lib"],"Error";
 					["js";"lib"],"Error";
@@ -457,6 +461,7 @@ let get_config com =
 			pf_static = false;
 			pf_static = false;
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_uses_utf16 = false;
 			pf_uses_utf16 = false;
+			pf_supports_rest_args = true;
 		}
 		}
 	| Neko ->
 	| Neko ->
 		{
 		{
@@ -477,6 +482,7 @@ let get_config com =
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_can_skip_non_nullable_argument = false;
 			pf_can_skip_non_nullable_argument = false;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
+			pf_supports_rest_args = true;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_native_throws = [
 				ec_native_throws = [
 					["flash";"errors"],"Error";
 					["flash";"errors"],"Error";
@@ -497,6 +503,7 @@ let get_config com =
 			default_config with
 			default_config with
 			pf_static = false;
 			pf_static = false;
 			pf_uses_utf16 = false;
 			pf_uses_utf16 = false;
+			pf_supports_rest_args = true;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_native_throws = [
 				ec_native_throws = [
 					["php"],"Throwable";
 					["php"],"Throwable";
@@ -533,6 +540,7 @@ let get_config com =
 			pf_pad_nulls = true;
 			pf_pad_nulls = true;
 			pf_overload = true;
 			pf_overload = true;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
+			pf_supports_rest_args = true;
 			pf_exceptions = {
 			pf_exceptions = {
 				ec_native_throws = [
 				ec_native_throws = [
 					["cs";"system"],"Exception";
 					["cs";"system"],"Exception";
@@ -558,6 +566,7 @@ let get_config com =
 			pf_pad_nulls = true;
 			pf_pad_nulls = true;
 			pf_overload = true;
 			pf_overload = true;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
+			pf_supports_rest_args = true;
 			pf_this_before_super = false;
 			pf_this_before_super = false;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_native_throws = [
 				ec_native_throws = [
@@ -587,6 +596,7 @@ let get_config com =
 			pf_capture_policy = CPLoopVars;
 			pf_capture_policy = CPLoopVars;
 			pf_uses_utf16 = false;
 			pf_uses_utf16 = false;
 			pf_supports_threads = true;
 			pf_supports_threads = true;
+			pf_supports_rest_args = true;
 			pf_exceptions = { default_config.pf_exceptions with
 			pf_exceptions = { default_config.pf_exceptions with
 				ec_native_throws = [
 				ec_native_throws = [
 					["python";"Exceptions"],"BaseException";
 					["python";"Exceptions"],"BaseException";

+ 5 - 5
src/core/ast.ml

@@ -98,6 +98,7 @@ type unop =
 	| Not
 	| Not
 	| Neg
 	| Neg
 	| NegBits
 	| NegBits
+	| Spread
 
 
 type string_literal_kind =
 type string_literal_kind =
 	| SDoubleQuotes
 	| SDoubleQuotes
@@ -135,6 +136,7 @@ type token =
 	| Question
 	| Question
 	| At
 	| At
 	| Dollar of string
 	| Dollar of string
+	| Spread
 
 
 type unop_flag =
 type unop_flag =
 	| Prefix
 	| Prefix
@@ -429,11 +431,7 @@ let get_own_doc_opt = Option.map_default (fun d -> d.doc_own) None
 
 
 let rec is_postfix (e,_) op = match op with
 let rec is_postfix (e,_) op = match op with
 	| Increment | Decrement | Not -> true
 	| Increment | Decrement | Not -> true
-	| Neg | NegBits -> false
-
-let is_prefix = function
-	| Increment | Decrement -> true
-	| Not | Neg | NegBits -> true
+	| Neg | NegBits | Spread -> false
 
 
 let base_class_name = snd
 let base_class_name = snd
 
 
@@ -562,6 +560,7 @@ let s_unop = function
 	| Not -> "!"
 	| Not -> "!"
 	| Neg -> "-"
 	| Neg -> "-"
 	| NegBits -> "~"
 	| NegBits -> "~"
+	| Spread -> "..."
 
 
 let s_token = function
 let s_token = function
 	| Eof -> "<end of file>"
 	| Eof -> "<end of file>"
@@ -587,6 +586,7 @@ let s_token = function
 	| Question -> "?"
 	| Question -> "?"
 	| At -> "@"
 	| At -> "@"
 	| Dollar v -> "$" ^ v
 	| Dollar v -> "$" ^ v
+	| Spread -> "..."
 
 
 exception Invalid_escape_sequence of char * int * (string option)
 exception Invalid_escape_sequence of char * int * (string option)
 
 

+ 1 - 0
src/core/json/genjson.ml

@@ -158,6 +158,7 @@ let generate_unop ctx op =
 		| Not -> "OpNot"
 		| Not -> "OpNot"
 		| Neg -> "OpNeg"
 		| Neg -> "OpNeg"
 		| NegBits -> "OpNegBits"
 		| NegBits -> "OpNegBits"
+		| Spread -> "OpSpread"
 	in
 	in
 	jstring name
 	jstring name
 
 

+ 5 - 0
src/core/tOther.ml

@@ -180,6 +180,11 @@ module ExtType = struct
 		| TAbstract({a_path=[],"Bool"},_) -> true
 		| TAbstract({a_path=[],"Bool"},_) -> true
 		| _ -> false
 		| _ -> false
 
 
+	let is_rest t = match t with
+		| TType({t_path=["haxe"; "extern"],"Rest"},_)
+		| TAbstract({a_path=["haxe"],"Rest"},_) -> true
+		| _ -> false
+
 	let is_type_param t =
 	let is_type_param t =
 		match t with
 		match t with
 		| TInst({ cl_kind = KTypeParameter _ }, _) -> true
 		| TInst({ cl_kind = KTypeParameter _ }, _) -> true

+ 3 - 1
src/generators/gencpp.ml

@@ -1819,7 +1819,7 @@ let rec cpp_type_of stack ctx haxe_type =
       | (("cpp"::["objc"]),"ObjcBlock"), [function_type] ->
       | (("cpp"::["objc"]),"ObjcBlock"), [function_type] ->
             let args,ret = (cpp_function_type_of_args_ret stack ctx function_type) in
             let args,ret = (cpp_function_type_of_args_ret stack ctx function_type) in
             TCppObjCBlock(args,ret)
             TCppObjCBlock(args,ret)
-      | (["haxe";"extern"], "Rest"),[rest] ->
+      | ((["cpp"]), "Rest"),[rest] ->
             TCppRest(cpp_type_of stack ctx rest)
             TCppRest(cpp_type_of stack ctx rest)
       | (("cpp"::["objc"]),"Protocol"), [interface_type] ->
       | (("cpp"::["objc"]),"Protocol"), [interface_type] ->
             (match follow interface_type with
             (match follow interface_type with
@@ -2986,6 +2986,7 @@ let retype_expression ctx request_type function_args function_type expression_tr
                | Neg -> CppUnop(CppNeg,e1)
                | Neg -> CppUnop(CppNeg,e1)
                | Not -> CppUnop(CppNot,e1)
                | Not -> CppUnop(CppNot,e1)
                | NegBits -> CppUnop(CppNegBits,e1)
                | NegBits -> CppUnop(CppNegBits,e1)
+               | Spread -> die ~p:expr.epos "Unexpected spread operator" __LOC__
             in reference, cpp_type_of expr.etype
             in reference, cpp_type_of expr.etype
 
 
          | TFor (v,init,block) ->
          | TFor (v,init,block) ->
@@ -7731,6 +7732,7 @@ class script_writer ctx filename asciiOut =
       | Decrement, _ -> IaMinusMinusPost
       | Decrement, _ -> IaMinusMinusPost
       | Not, _ -> IaLogicNot
       | Not, _ -> IaLogicNot
       | Neg, _ -> IaNeg
       | Neg, _ -> IaNeg
+      | Spread, _ -> die ~p:e.epos "Unexpected spread operator" __LOC__
       | NegBits, _ -> IaBitNot );
       | NegBits, _ -> IaBitNot );
       this#gen_expression e;
       this#gen_expression e;
    (* TODO - lval op-assign local/member/array *)
    (* TODO - lval op-assign local/member/array *)

+ 5 - 1
src/generators/gencs.ml

@@ -952,7 +952,8 @@ let generate con =
 					| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
 					| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
 					| TAbstract( { a_path = [],"Class" }, _ )
 					| TAbstract( { a_path = [],"Class" }, _ )
 					| TAbstract( { a_path = [],"Enum" }, _ )
 					| TAbstract( { a_path = [],"Enum" }, _ )
-					| TAbstract( { a_path = ["haxe";"extern"],"Rest" }, _ )
+					| TAbstract( { a_path = (["haxe"]),"Rest" }, _ )
+					| TType( { t_path = (["haxe";"extern"]),"Rest" }, _ )
 					| TInst( { cl_path = ([], "Class") }, _ )
 					| TInst( { cl_path = ([], "Class") }, _ )
 					| TInst( { cl_path = ([], "Enum") }, _ ) -> TInst(ttype,[])
 					| TInst( { cl_path = ([], "Enum") }, _ ) -> TInst(ttype,[])
 					| TInst( ({ cl_kind = KTypeParameter _ } as cl), _ ) when erase_generics && not (Meta.has Meta.NativeGeneric cl.cl_meta) ->
 					| TInst( ({ cl_kind = KTypeParameter _ } as cl), _ ) when erase_generics && not (Meta.has Meta.NativeGeneric cl.cl_meta) ->
@@ -1637,6 +1638,8 @@ let generate con =
 						(match flag with
 						(match flag with
 							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " " ); expr_s w e
 							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " " ); expr_s w e
 							| Ast.Postfix -> expr_s w e; write w (Ast.s_unop op))
 							| Ast.Postfix -> expr_s w e; write w (Ast.s_unop op))
+					| TUnop (Spread, Prefix, e) ->
+						expr_s w e
 					| TUnop (op, flag, e) ->
 					| TUnop (op, flag, e) ->
 						(match flag with
 						(match flag with
 							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " (" ); expr_s w e; write w ") "
 							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " (" ); expr_s w e; write w ") "
@@ -1955,6 +1958,7 @@ let generate con =
 				| TAbstract ({ a_path = (["cs"], "Ref") },[t]) -> "ref " ^ t_s t
 				| TAbstract ({ a_path = (["cs"], "Ref") },[t]) -> "ref " ^ t_s t
 				| TType ({ t_path = (["cs"], "Out") }, [t])
 				| TType ({ t_path = (["cs"], "Out") }, [t])
 				| TAbstract ({ a_path = (["cs"], "Out") },[t]) -> "out " ^ t_s t
 				| TAbstract ({ a_path = (["cs"], "Out") },[t]) -> "out " ^ t_s t
+				| _ when ExtType.is_rest (Type.follow t) -> "params " ^ (t_s (Abstract.follow_with_abstracts t))
 				| t -> t_s t
 				| t -> t_s t
 			in
 			in
 			let c = contents w in
 			let c = contents w in

+ 2 - 0
src/generators/genhl.ml

@@ -2497,6 +2497,8 @@ and eval_expr ctx e =
 		let r = eval_to ctx v t in
 		let r = eval_to ctx v t in
 		op ctx (ONeg (tmp,r));
 		op ctx (ONeg (tmp,r));
 		tmp
 		tmp
+	| TUnop (Spread,_,_) ->
+		die ~p:e.epos "Unexpected spread operator" __LOC__
 	| TUnop (NegBits,_,v) ->
 	| TUnop (NegBits,_,v) ->
 		let t = to_type ctx e.etype in
 		let t = to_type ctx e.etype in
 		let tmp = alloc_tmp ctx t in
 		let tmp = alloc_tmp ctx t in

+ 31 - 6
src/generators/genjava.ml

@@ -1666,6 +1666,7 @@ let generate con =
 						acc + 1
 						acc + 1
 					) 0 el);
 					) 0 el);
 					write w ")"
 					write w ")"
+				| TUnop (Ast.Spread, Prefix, e) -> expr_s w e
 				| TUnop ((Ast.Increment as op), flag, e)
 				| TUnop ((Ast.Increment as op), flag, e)
 				| TUnop ((Ast.Decrement as op), flag, e) ->
 				| TUnop ((Ast.Decrement as op), flag, e) ->
 					(match flag with
 					(match flag with
@@ -1994,9 +1995,19 @@ let generate con =
 				let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in
 				let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in
 
 
 				let params = List.map snd cl.cl_params in
 				let params = List.map snd cl.cl_params in
-				let ret_type, args = match follow cf_type, follow cf.cf_type with
+				let ret_type, args, has_rest_args = match follow cf_type, follow cf.cf_type with
 					| TFun (strbtl, t), TFun(rargs, _) ->
 					| TFun (strbtl, t), TFun(rargs, _) ->
-							(apply_params cl.cl_params params (real_type t), List.map2 (fun(_,_,t) (n,o,_) -> (n,o,apply_params cl.cl_params params (real_type t))) strbtl rargs)
+						let ret_type = apply_params cl.cl_params params (real_type t)
+						and args =
+							List.map2 (fun(_,_,t) (n,o,_) ->
+								(n,o,apply_params cl.cl_params params (real_type t))
+							) strbtl rargs
+						and rest =
+							match List.rev rargs with
+							| (_,_,t) :: _ -> ExtType.is_rest (follow t)
+							| _ -> false
+						in
+						ret_type,args,rest
 					| _ -> die "" __LOC__
 					| _ -> die "" __LOC__
 				in
 				in
 
 
@@ -2008,12 +2019,26 @@ let generate con =
 				write_parts w (visibility :: v_n :: modifiers @ [params; (if is_new then "" else rett_s cf.cf_pos (run_follow gen ret_type)); (change_field name)]);
 				write_parts w (visibility :: v_n :: modifiers @ [params; (if is_new then "" else rett_s cf.cf_pos (run_follow gen ret_type)); (change_field name)]);
 
 
 				(* <T>(string arg1, object arg2) with T : object *)
 				(* <T>(string arg1, object arg2) with T : object *)
-				(match cf.cf_expr with
+				let arg_names =
+					match cf.cf_expr with
 					| Some { eexpr = TFunction tf } ->
 					| Some { eexpr = TFunction tf } ->
-							print w "(%s)" (String.concat ", " (List.map2 (fun (var,_) (_,_,t) -> sprintf "%s %s" (argt_s cf.cf_pos (run_follow gen t)) (change_id var.v_name)) tf.tf_args args))
+						List.map (fun (var,_) -> change_id var.v_name) tf.tf_args
 					| _ ->
 					| _ ->
-							print w "(%s)" (String.concat ", " (List.map (fun (name, _, t) -> sprintf "%s %s" (argt_s cf.cf_pos (run_follow gen t)) (change_id name)) args))
-				);
+						List.map (fun (name,_,_) -> change_id name) args
+				in
+				let rec loop acc names args =
+					match names, args with
+					| [], [] -> acc
+					| _, [] | [], _ ->
+						die "" __LOC__
+					| [name], [_,_,TInst ({ cl_path = ["java"],"NativeArray" }, [t])] when has_rest_args ->
+						let arg = sprintf "%s ...%s" (argt_s cf.cf_pos (run_follow gen t)) name in
+						arg :: acc
+					| name :: names, (_,_,t) :: args ->
+						let arg = sprintf "%s %s" (argt_s cf.cf_pos (run_follow gen t)) name in
+						loop (arg :: acc) names args
+				in
+				print w "(%s)" (String.concat ", " (List.rev (loop [] arg_names args)));
 				if is_interface || List.mem "native" modifiers || is_abstract then
 				if is_interface || List.mem "native" modifiers || is_abstract then
 					write w ";"
 					write w ";"
 				else begin
 				else begin

+ 128 - 18
src/generators/genjs.ml

@@ -326,6 +326,42 @@ let rec concat ctx s f = function
 		spr ctx s;
 		spr ctx s;
 		concat ctx s f l
 		concat ctx s f l
 
 
+(**
+	Produce expressions to declare arguments of a function with `Rest<T>` trailing argument.
+	Used for ES5 and older standards, which don't support "rest parameters" syntax.
+	`args` is a list of explicitly defined arguments.
+	`rest_arg` is the argument of `Rest<T>` type.
+
+	This implementation copies rest arguments into a new array in a loop.
+	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
+	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
+	]
+
 let fun_block ctx f p =
 let fun_block ctx f p =
 	let e = List.fold_left (fun e (a,c) ->
 	let e = List.fold_left (fun e (a,c) ->
 		match c with
 		match c with
@@ -396,12 +432,28 @@ let var ctx =
 	if ctx.es_version >= 6 then "let" else "var"
 	if ctx.es_version >= 6 then "let" else "var"
 
 
 let rec gen_call ctx e el in_value =
 let rec gen_call ctx e el in_value =
+	let apply,el =
+		if ctx.es_version < 6 then
+			match List.rev el with
+			| [{ eexpr = TUnop (Spread,Ast.Prefix,rest) }] ->
+				true,[rest]
+			| { eexpr = TUnop (Spread,Ast.Prefix,rest) } :: args_rev ->
+				(* [arg1, arg2, ..., argN].concat(rest) *)
+				let arr = mk (TArrayDecl (List.rev args_rev)) t_dynamic null_pos in
+				let concat = mk (TField (arr, FDynamic "concat")) t_dynamic null_pos in
+				true,[mk (TCall (concat, [rest])) t_dynamic null_pos]
+			| _ ->
+				false,el
+		else
+			false,el
+	in
 	match e.eexpr , el with
 	match e.eexpr , el with
 	| TConst TSuper , params when ctx.es_version < 6 ->
 	| TConst TSuper , params when ctx.es_version < 6 ->
 		(match ctx.current.cl_super with
 		(match ctx.current.cl_super with
 		| None -> abort "Missing api.setCurrentClass" e.epos
 		| None -> abort "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 		| Some (c,_) ->
-			print ctx "%s.call(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
+			let call = if apply then "apply" else "call" in
+			print ctx "%s.%s(%s" (ctx.type_accessor (TClassDecl c)) call (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 			spr ctx ")";
 		);
 		);
@@ -410,17 +462,22 @@ let rec gen_call ctx e el in_value =
 		| None -> abort "Missing api.setCurrentClass" e.epos
 		| None -> abort "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 		| Some (c,_) ->
 			let name = field_name f in
 			let name = field_name f in
-			print ctx "%s.prototype%s.call(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
+			let call = if apply then "apply" else "call" in
+			print ctx "%s.prototype%s.%s(%s" (ctx.type_accessor (TClassDecl c)) (field name) call (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 			spr ctx ")";
 		);
 		);
 	| TCall (x,_) , el when not (is_code_injection_function x) ->
 	| TCall (x,_) , el when not (is_code_injection_function x) ->
-		spr ctx "(";
-		gen_value ctx e;
-		spr ctx ")";
-		spr ctx "(";
-		concat ctx "," (gen_value ctx) el;
-		spr ctx ")";
+		if apply then
+			gen_call_with_apply ctx e el
+		else begin
+			spr ctx "(";
+			gen_value ctx e;
+			spr ctx ")";
+			spr ctx "(";
+			concat ctx "," (gen_value ctx) el;
+			spr ctx ")";
+		end
 	| TField (_, FStatic ({ cl_path = ["js"],"Syntax" }, { cf_name = meth })), args ->
 	| TField (_, FStatic ({ cl_path = ["js"],"Syntax" }, { cf_name = meth })), args ->
 		gen_syntax ctx meth args e.epos
 		gen_syntax ctx meth args e.epos
 	| TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "rethrow" })), [] ->
 	| TField (_, FStatic ({ cl_path = ["js"],"Lib" }, { cf_name = "rethrow" })), [] ->
@@ -507,9 +564,32 @@ let rec gen_call ctx e el in_value =
 		gen_value ctx x;
 		gen_value ctx x;
 		print ctx ")";
 		print ctx ")";
 	| _ ->
 	| _ ->
-		gen_value ctx e;
-		spr ctx "(";
-		concat ctx "," (gen_value ctx) el;
+		if apply then
+			gen_call_with_apply ctx e el
+		else begin
+			gen_value ctx e;
+			spr ctx "(";
+			concat ctx "," (gen_value ctx) el;
+			spr ctx ")"
+		end
+
+and gen_call_with_apply ctx target args =
+	(match args with
+	| [_] -> ()
+	| _ -> die ~p:target.epos "`args` for `gen_call_with_apply` must contain exactly one item" __LOC__
+	);
+	match target.eexpr with
+	| TField (this, (FInstance (_,_,{ cf_name = field }) | FAnon { cf_name = field } | FDynamic field | FClosure (_,{ cf_name = field }))) ->
+		add_feature ctx "thisForCallWithRestArgs";
+		spr ctx "($_=";
+		gen_value ctx this;
+		spr ctx (",$_." ^ field ^ ".apply($_,");
+		concat ctx "," (gen_value ctx) args;
+		spr ctx "))"
+	| _ ->
+		gen_value ctx target;
+		spr ctx ".apply(null,";
+		concat ctx "," (gen_value ctx) args;
 		spr ctx ")"
 		spr ctx ")"
 
 
 (*
 (*
@@ -822,10 +902,34 @@ and gen_function ?(keyword="function") ctx f pos =
 	let old = ctx.in_value, ctx.in_loop in
 	let old = ctx.in_value, ctx.in_loop in
 	ctx.in_value <- None;
 	ctx.in_value <- None;
 	ctx.in_loop <- false;
 	ctx.in_loop <- false;
-	let args = List.map (fun (v,_) ->
-		check_var_declaration v;
-		ident v.v_name
-	) f.tf_args in
+	let f,args =
+		match List.rev f.tf_args with
+		| (v,None) :: args_rev when ExtType.is_rest (follow v.v_type) ->
+			(* Use ES6 rest args syntax: `...arg` *)
+			if ctx.es_version >= 6 then
+				f, List.map (fun (a,_) ->
+					check_var_declaration a;
+					if a == v then ("..." ^ ident a.v_name)
+					else ident a.v_name
+				) 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 body =
+					let el =
+						match f.tf_expr.eexpr with
+						| TBlock el -> args_decl @ el
+						| _ -> 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, List.map (fun (v,_) ->
+				check_var_declaration v;
+				ident v.v_name
+			) f.tf_args
+	in
 	print ctx "%s(%s) " keyword (String.concat "," args);
 	print ctx "%s(%s) " keyword (String.concat "," args);
 	gen_expr ctx (fun_block ctx f pos);
 	gen_expr ctx (fun_block ctx f pos);
 	ctx.in_value <- fst old;
 	ctx.in_value <- fst old;
@@ -1876,9 +1980,9 @@ let generate com =
 	let vars = if (enums_as_objects && (has_feature ctx "has_enum" || has_feature ctx "Type.resolveEnum")) then "$hxEnums = $hxEnums || {}" :: vars else vars in
 	let vars = if (enums_as_objects && (has_feature ctx "has_enum" || has_feature ctx "Type.resolveEnum")) then "$hxEnums = $hxEnums || {}" :: vars else vars in
 	let vars,has_dollar_underscore =
 	let vars,has_dollar_underscore =
 		if List.exists (function TEnumDecl { e_extern = false } -> true | _ -> false) com.types then
 		if List.exists (function TEnumDecl { e_extern = false } -> true | _ -> false) com.types then
-			"$_" :: vars,true
+			"$_" :: vars,ref true
 		else
 		else
-			vars,false
+			vars,ref false
 	in
 	in
 	(match List.rev vars with
 	(match List.rev vars with
 	| [] -> ()
 	| [] -> ()
@@ -1938,9 +2042,10 @@ let generate com =
 	end;
 	end;
 	if has_feature ctx "use.$bind" then begin
 	if has_feature ctx "use.$bind" then begin
 		add_feature ctx "$global.$haxeUID";
 		add_feature ctx "$global.$haxeUID";
-		if not has_dollar_underscore then begin
+		if not !has_dollar_underscore then begin
 			print ctx "var $_";
 			print ctx "var $_";
 			newline ctx;
 			newline ctx;
+			has_dollar_underscore := true
 		end;
 		end;
 		(if ctx.es_version < 5 then
 		(if ctx.es_version < 5 then
 			print ctx "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; o.hx__closures__[m.__id__] = f; } return f; }"
 			print ctx "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; o.hx__closures__[m.__id__] = f; } return f; }"
@@ -1957,6 +2062,11 @@ let generate com =
 		add_feature ctx "js.Lib.global";
 		add_feature ctx "js.Lib.global";
 		print ctx "$global.$haxeUID |= 0;\n";
 		print ctx "$global.$haxeUID |= 0;\n";
 	end;
 	end;
+	if not !has_dollar_underscore && has_feature ctx "thisForCallWithRestArgs" then begin
+		print ctx "var $_";
+		newline ctx;
+		has_dollar_underscore := true
+	end;
 	List.iter (gen_block_element ~newline_after:true ~keep_blocks:(ctx.es_version >= 6) ctx) (List.rev ctx.inits);
 	List.iter (gen_block_element ~newline_after:true ~keep_blocks:(ctx.es_version >= 6) ctx) (List.rev ctx.inits);
 	List.iter (generate_static ctx) (List.rev ctx.statics);
 	List.iter (generate_static ctx) (List.rev ctx.statics);
 	(match com.main with
 	(match com.main with

+ 11 - 3
src/generators/genjvm.ml

@@ -124,11 +124,12 @@ let rec jsignature_of_type gctx stack t =
 				| [t] -> get_boxed_type (jsignature_of_type t)
 				| [t] -> get_boxed_type (jsignature_of_type t)
 				| _ -> die "" __LOC__
 				| _ -> die "" __LOC__
 				end
 				end
-			| (["haxe";"ds"],"Vector") | (["haxe";"extern"],"Rest") ->
+			| ["haxe";"ds"],"Vector" ->
 				begin match tl with
 				begin match tl with
 				| [t] -> TArray(jsignature_of_type t,None)
 				| [t] -> TArray(jsignature_of_type t,None)
 				| _ -> die "" __LOC__
 				| _ -> die "" __LOC__
 				end
 				end
+			| ["haxe"],"Rest" -> TArray(object_sig,None)
 			| [],"Dynamic" ->
 			| [],"Dynamic" ->
 				object_sig
 				object_sig
 			| [],("Class" | "Enum") ->
 			| [],("Class" | "Enum") ->
@@ -1359,6 +1360,8 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 				(self#condition false e)
 				(self#condition false e)
 				(fun () -> code#bconst false)
 				(fun () -> code#bconst false)
 				(fun () -> code#bconst true)
 				(fun () -> code#bconst true)
+		| Spread, _ ->
+			self#texpr (rvalue_type gctx e.etype) e
 		| NegBits,_ ->
 		| NegBits,_ ->
 			let jsig = jsignature_of_type gctx (follow e.etype) in
 			let jsig = jsignature_of_type gctx (follow e.etype) in
 			self#texpr rvalue_any e;
 			self#texpr rvalue_any e;
@@ -1388,8 +1391,13 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return
 			| (_,_,t) :: tl,e :: el ->
 			| (_,_,t) :: tl,e :: el ->
 				let jsig = jsignature_of_type gctx t in
 				let jsig = jsignature_of_type gctx t in
 				begin match tl,Type.follow t with
 				begin match tl,Type.follow t with
-				| [],(TAbstract({a_path = ["haxe";"extern"],"Rest"},[t])) ->
-					self#new_native_array (jsignature_of_type gctx t) (e :: el);
+				| [],(TAbstract({a_path = ["haxe"],"Rest"},[t1])) ->
+					(match e.eexpr with
+					| TUnop (Spread,_,e) ->
+						self#texpr (rvalue_sig jsig) e
+					| _ ->
+						self#new_native_array (get_boxed_type (jsignature_of_type gctx t1)) (e :: el)
+					);
 					List.rev (jsig :: acc)
 					List.rev (jsig :: acc)
 				| _ ->
 				| _ ->
 					self#texpr (rvalue_sig jsig) e;
 					self#texpr (rvalue_sig jsig) e;

+ 48 - 18
src/generators/genlua.ml

@@ -185,13 +185,32 @@ let rec concat ctx s f = function
         spr ctx s;
         spr ctx s;
         concat ctx s f l
         concat ctx s f l
 
 
+(* create a __lua__ call *)
+let mk_lua_code com code args t pos =
+    let lua_local = mk (TIdent "__lua__") t_dynamic pos in
+    let code_const = Texpr.Builder.make_string com code pos in
+    mk (TCall (lua_local, code_const :: args)) t pos
+
+let inject_rest_args ctx args e =
+    match List.rev args with
+    | (v,_) :: _ when ExtType.is_rest (follow v.v_type) ->
+        let rest = mk (TLocal v) v.v_type v.v_pos in
+        let init_rest = mk_lua_code ctx.com.basic "local {0} = {...}" [rest] ctx.com.basic.tvoid v.v_pos in
+        (match e.eexpr with
+        | TBlock el ->
+            { e with eexpr = TBlock (init_rest :: el) }
+        | _ ->
+            { e with eexpr = TBlock [init_rest; e] }
+        )
+    | _ -> e
+
 let fun_block ctx f p =
 let fun_block ctx f p =
-    let e = List.fold_left (fun e (a,c) ->
+    let fn_body = inject_rest_args ctx f.tf_args f.tf_expr in
+    List.fold_left (fun e (a,c) ->
         match c with
         match c with
         | None | Some {eexpr = TConst TNull} -> e
         | None | Some {eexpr = TConst TNull} -> e
         | Some c -> Type.concat (Texpr.set_default ctx.com.basic a c p) e
         | Some c -> Type.concat (Texpr.set_default ctx.com.basic a c p) e
-    ) f.tf_expr f.tf_args in
-    e
+    ) fn_body f.tf_args
 
 
 let open_block ctx =
 let open_block ctx =
     let oldt = ctx.tabs in
     let oldt = ctx.tabs in
@@ -223,12 +242,6 @@ let index_of f l =
     in
     in
     find l 0
     find l 0
 
 
-(* create a __lua__ call *)
-let mk_lua_code com code args t pos =
-    let lua_local = mk (TIdent "__lua__") t_dynamic pos in
-    let code_const = Texpr.Builder.make_string com code pos in
-    mk (TCall (lua_local, code_const :: args)) t pos
-
 (* create a multi-return boxing call for given expr *)
 (* create a multi-return boxing call for given expr *)
 let mk_mr_box ctx e =
 let mk_mr_box ctx e =
     let s_fields =
     let s_fields =
@@ -618,13 +631,25 @@ and check_multireturn_param ctx t pos =
         | _ ->
         | _ ->
                 ();
                 ();
 
 
+(* for declaring identifiers in values/blocks *)
+and lua_ident_name a =
+    match a.v_name, a.v_kind, a.v_type with
+        | "this", _, _ -> "self";
+        | _, _, _ ->  ident a.v_name;
+
+
+(* for declaring arguments in function defintions *)
+and lua_arg_name(a,_) =
+    match a.v_name, a.v_kind, a.v_type with
+        | "this", _, _ -> "self";
+        | _, _, TAbstract({a_path=["haxe"],"Rest" },_) -> "...";
+        | _, _, _ ->  ident a.v_name;
+
 and gen_expr ?(local=true) ctx e = begin
 and gen_expr ?(local=true) ctx e = begin
     match e.eexpr with
     match e.eexpr with
       TConst c ->
       TConst c ->
         gen_constant ctx e.epos c;
         gen_constant ctx e.epos c;
-    | TLocal v when v.v_name = "this" ->
-        spr ctx "self";
-    | TLocal v -> spr ctx (ident v.v_name)
+    | TLocal v -> spr ctx (lua_ident_name v);
     | TArray (e1,{ eexpr = TConst (TString s) }) when valid_lua_ident s && (match e1.eexpr with TConst (TInt _|TFloat _) -> false | _ -> true) ->
     | TArray (e1,{ eexpr = TConst (TString s) }) when valid_lua_ident s && (match e1.eexpr with TConst (TInt _|TFloat _) -> false | _ -> true) ->
         gen_value ctx e1;
         gen_value ctx e1;
         spr ctx (field s)
         spr ctx (field s)
@@ -724,7 +749,7 @@ and gen_expr ?(local=true) ctx e = begin
         let old = ctx.in_value, ctx.in_loop in
         let old = ctx.in_value, ctx.in_loop in
         ctx.in_value <- None;
         ctx.in_value <- None;
         ctx.in_loop <- false;
         ctx.in_loop <- false;
-        print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+        print ctx "function(%s) " (String.concat "," (List.map lua_arg_name f.tf_args));
         let fblock = fun_block ctx f e.epos in
         let fblock = fun_block ctx f e.epos in
         (match fblock.eexpr with
         (match fblock.eexpr with
          | TBlock el ->
          | TBlock el ->
@@ -906,6 +931,11 @@ and gen_expr ?(local=true) ctx e = begin
         spr ctx "_hx_bit.bnot(";
         spr ctx "_hx_bit.bnot(";
         gen_value ctx e;
         gen_value ctx e;
         spr ctx ")";
         spr ctx ")";
+    | TUnop (Spread,Prefix,e) ->
+        add_feature ctx "use._hx_table";
+        spr ctx "_hx_table.unpack(";
+        gen_value ctx e;
+        spr ctx ")";
     | TUnop (op,Ast.Prefix,e) ->
     | TUnop (op,Ast.Prefix,e) ->
         spr ctx (Ast.s_unop op);
         spr ctx (Ast.s_unop op);
         gen_value ctx e
         gen_value ctx e
@@ -1079,7 +1109,7 @@ and gen_anon_value ctx e =
         let old = ctx.in_value, ctx.in_loop in
         let old = ctx.in_value, ctx.in_loop in
         ctx.in_value <- None;
         ctx.in_value <- None;
         ctx.in_loop <- false;
         ctx.in_loop <- false;
-        print ctx "function(%s) " (String.concat "," ("self" :: (List.map ident (List.map arg_name f.tf_args))));
+        print ctx "function(%s) " (String.concat "," ("self" :: (List.map lua_arg_name f.tf_args)));
         let fblock = fun_block ctx f e.epos in
         let fblock = fun_block ctx f e.epos in
         (match fblock.eexpr with
         (match fblock.eexpr with
          | TBlock el ->
          | TBlock el ->
@@ -1510,7 +1540,7 @@ let gen_class_field ctx c f =
              ctx.in_value <- None;
              ctx.in_value <- None;
              ctx.in_loop <- false;
              ctx.in_loop <- false;
              print ctx " = function";
              print ctx " = function";
-             print ctx "(%s) " (String.concat "," ("self" :: List.map ident (List.map arg_name f2.tf_args)));
+             print ctx "(%s) " (String.concat "," ("self" ::(List.map lua_arg_name f2.tf_args)));
              let fblock = fun_block ctx f2 e.epos in
              let fblock = fun_block ctx f2 e.epos in
              (match fblock.eexpr with
              (match fblock.eexpr with
               | TBlock el ->
               | TBlock el ->
@@ -1572,7 +1602,7 @@ let generate_class ctx c =
                    let old = ctx.in_value, ctx.in_loop in
                    let old = ctx.in_value, ctx.in_loop in
                    ctx.in_value <- None;
                    ctx.in_value <- None;
                    ctx.in_loop <- false;
                    ctx.in_loop <- false;
-                   print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+                   print ctx "function(%s) " (String.concat "," (List.map lua_arg_name f.tf_args));
                    let fblock = fun_block ctx f e.epos in
                    let fblock = fun_block ctx f e.epos in
                    (match fblock.eexpr with
                    (match fblock.eexpr with
                     | TBlock el ->
                     | TBlock el ->
@@ -1580,13 +1610,13 @@ let generate_class ctx c =
                         newline ctx;
                         newline ctx;
                         if not (has_prototype ctx c) then println ctx "local self = _hx_new()" else
                         if not (has_prototype ctx c) then println ctx "local self = _hx_new()" else
                             println ctx "local self = _hx_new(%s.prototype)" p;
                             println ctx "local self = _hx_new(%s.prototype)" p;
-                        println ctx "%s.super(%s)" p (String.concat "," ("self" :: (List.map ident (List.map arg_name f.tf_args))));
+                        println ctx "%s.super(%s)" p (String.concat "," ("self" :: (List.map lua_arg_name f.tf_args)));
                         if p = "String" then println ctx "self = string";
                         if p = "String" then println ctx "self = string";
                         spr ctx "return self";
                         spr ctx "return self";
                         bend(); newline ctx;
                         bend(); newline ctx;
                         spr ctx "end"; newline ctx;
                         spr ctx "end"; newline ctx;
                         let bend = open_block ctx in
                         let bend = open_block ctx in
-                        print ctx "%s.super = function(%s) " p (String.concat "," ("self" :: (List.map ident (List.map arg_name f.tf_args))));
+                        print ctx "%s.super = function(%s) " p (String.concat "," ("self" :: (List.map lua_arg_name f.tf_args)));
                         List.iter (gen_block_element ctx) el;
                         List.iter (gen_block_element ctx) el;
                         bend();
                         bend();
                         newline ctx;
                         newline ctx;

+ 2 - 1
src/generators/genneko.ml

@@ -189,6 +189,7 @@ and gen_unop ctx p op flag e =
 	| Not -> call p (builtin p "not") [gen_expr ctx e]
 	| Not -> call p (builtin p "not") [gen_expr ctx e]
 	| Neg -> (EBinop ("-",int p 0, gen_expr ctx e),p)
 	| Neg -> (EBinop ("-",int p 0, gen_expr ctx e),p)
 	| NegBits -> (EBinop ("-",int p (-1), gen_expr ctx e),p)
 	| NegBits -> (EBinop ("-",int p (-1), gen_expr ctx e),p)
+	| Spread -> die ~p:e.epos "Unhandled spread operator" __LOC__
 
 
 and gen_call ctx p e el =
 and gen_call ctx p e el =
 	match e.eexpr , el with
 	match e.eexpr , el with
@@ -266,7 +267,7 @@ and gen_expr ctx e =
 		call p (field p (ident p "Array") "new1") [array p (List.map (gen_expr ctx) el); int p (List.length el)]
 		call p (field p (ident p "Array") "new1") [array p (List.map (gen_expr ctx) el); int p (List.length el)]
 	| TCall (e,el) ->
 	| TCall (e,el) ->
 		gen_call ctx p e el
 		gen_call ctx p e el
-	| TNew (c,_,params) ->
+	| TNew (c,tl,params) ->
 		call p (field p (gen_type_path p c.cl_path) "new") (List.map (gen_expr ctx) params)
 		call p (field p (gen_type_path p c.cl_path) "new") (List.map (gen_expr ctx) params)
 	| TUnop (op,flag,e) ->
 	| TUnop (op,flag,e) ->
 		gen_unop ctx p op flag e
 		gen_unop ctx p op flag e

+ 57 - 4
src/generators/genphp7.ml

@@ -308,6 +308,11 @@ let is_string expr = ExtType.is_string (follow expr.etype)
 *)
 *)
 let is_array_type t = match follow t with TInst ({ cl_path = ([], "Array") }, _) -> true | _ -> false
 let is_array_type t = match follow t with TInst ({ cl_path = ([], "Array") }, _) -> true | _ -> false
 
 
+(**
+	Check if specified type is haxe.Rest
+*)
+let is_rest_type t = ExtType.is_rest (Type.follow t)
+
 (**
 (**
 	Check if specified type represents a function
 	Check if specified type represents a function
 *)
 *)
@@ -702,6 +707,15 @@ let is_access expr =
 		| TField _ | TArray _ -> true
 		| TField _ | TArray _ -> true
 		| _ -> false
 		| _ -> false
 
 
+(**
+	Check if specified field access is an access to the field `Array.arr`
+	It's a private field of the php-specific implementation of Haxe Array.
+*)
+let is_array_arr faccess =
+	match faccess with
+		| FInstance ({ cl_path = [],"Array" }, _, { cf_name = "arr" }) -> true
+		| _ -> false
+
 (**
 (**
 	Indicates if `expr` is actually a call to Haxe->PHP magic function
 	Indicates if `expr` is actually a call to Haxe->PHP magic function
 	@see http://old.haxe.org/doc/advanced/magic#php-magic
 	@see http://old.haxe.org/doc/advanced/magic#php-magic
@@ -1440,6 +1454,30 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 			match self#parent_expr with
 			match self#parent_expr with
 				| Some { eexpr = TCall _ } -> true
 				| Some { eexpr = TCall _ } -> true
 				| _ -> false
 				| _ -> false
+		(**
+			Indicates if current expression is passed to `php.Ref<T>`
+		*)
+		method current_expr_is_for_ref =
+			match expr_hierarchy with
+				| [] -> false
+				| current :: _ ->
+					match self#parent_expr with
+						| Some { eexpr = TCall (target, params) } when current != (reveal_expr target) ->
+							(match follow target.etype with
+								| TFun (args,_) ->
+									let rec check args params =
+										match args, params with
+										| (_, _, t) :: _, param :: _ when current == (reveal_expr param) ->
+											is_ref t
+										| _, [] | [], _ ->
+											false
+										| _ :: args, _ :: params ->
+											check args params
+									in
+									check args params
+								| _ -> false
+							)
+						| _ -> false
 		(**
 		(**
 			Check if currently generated expression is located in a left part of assignment.
 			Check if currently generated expression is located in a left part of assignment.
 		*)
 		*)
@@ -1579,6 +1617,8 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 				| TBinop (operation, expr1, expr2) when needs_dereferencing (is_assignment_binop operation) expr1 ->
 				| TBinop (operation, expr1, expr2) when needs_dereferencing (is_assignment_binop operation) expr1 ->
 					self#write_expr { expr with eexpr = TBinop (operation, self#dereference expr1, expr2) }
 					self#write_expr { expr with eexpr = TBinop (operation, self#dereference expr1, expr2) }
 				| TBinop (operation, expr1, expr2) -> self#write_expr_binop operation expr1 expr2
 				| TBinop (operation, expr1, expr2) -> self#write_expr_binop operation expr1 expr2
+				| TField ({ eexpr = TArrayDecl exprs }, faccess) when is_array_arr faccess && not self#current_expr_is_for_ref ->
+					self#write_native_array_decl exprs
 				| TField (fexpr, access) when is_php_global expr -> self#write_expr_php_global expr
 				| TField (fexpr, access) when is_php_global expr -> self#write_expr_php_global expr
 				| TField (fexpr, access) when is_php_class_const expr -> self#write_expr_php_class_const expr
 				| TField (fexpr, access) when is_php_class_const expr -> self#write_expr_php_class_const expr
 				| TField (fexpr, access) when needs_dereferencing (self#is_in_write_context) expr -> self#write_expr (self#dereference expr)
 				| TField (fexpr, access) when needs_dereferencing (self#is_in_write_context) expr -> self#write_expr (self#dereference expr)
@@ -1660,16 +1700,27 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 						| _ ->
 						| _ ->
 							decl()
 							decl()
 					)
 					)
+				| _ ->
+					self#write ((self#use array_type_path) ^ "::wrap(");
+					self#write_native_array_decl exprs;
+					self#write ")"
+		(**
+			Writes native array declaration to output buffer
+		*)
+		method write_native_array_decl exprs =
+			match exprs with
+				| [] ->
+					self#write "[]";
 				| [expr] ->
 				| [expr] ->
-					self#write ((self#use array_type_path) ^ "::wrap([");
+					self#write "[";
 					self#write_expr expr;
 					self#write_expr expr;
-					self#write "])"
+					self#write "]"
 				| _ ->
 				| _ ->
-					self#write ((self#use array_type_path) ^ "::wrap([\n");
+					self#write "[\n";
 					self#indent_more;
 					self#indent_more;
 					List.iter (fun expr -> self#write_array_item ~separate_line:true expr) exprs;
 					List.iter (fun expr -> self#write_array_item ~separate_line:true expr) exprs;
 					self#indent_less;
 					self#indent_less;
-					self#write_with_indentation "])"
+					self#write_with_indentation "]"
 		(**
 		(**
 			Write associative array declaration
 			Write associative array declaration
 		*)
 		*)
@@ -2123,6 +2174,7 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 				| Postfix ->
 				| Postfix ->
 					self#write_expr expr;
 					self#write_expr expr;
 					self#write (Ast.s_unop operation)
 					self#write (Ast.s_unop operation)
+
 		method private write_expr_for_field_access expr access_str field_str =
 		method private write_expr_for_field_access expr access_str field_str =
 			let access_str = ref access_str in
 			let access_str = ref access_str in
 			(match (reveal_expr expr).eexpr with
 			(match (reveal_expr expr).eexpr with
@@ -2845,6 +2897,7 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name =
 			match arg with
 			match arg with
 				| ({ v_name = arg_name; v_type = arg_type }, default_value) ->
 				| ({ v_name = arg_name; v_type = arg_type }, default_value) ->
 					vars#declared (vname arg_name);
 					vars#declared (vname arg_name);
+					if is_rest_type arg_type then self#write "...";
 					if is_ref arg_type then self#write "&";
 					if is_ref arg_type then self#write "&";
 					self#write ("$" ^ (vname arg_name));
 					self#write ("$" ^ (vname arg_name));
 					match default_value with
 					match default_value with

+ 2 - 1
src/generators/genpy.ml

@@ -1058,6 +1058,7 @@ module Printer = struct
 		| Not -> "not "
 		| Not -> "not "
 		| Neg -> "-";
 		| Neg -> "-";
 		| NegBits -> "~"
 		| NegBits -> "~"
+		| Spread -> "*"
 
 
 	let print_binop = function
 	let print_binop = function
 		| OpAdd -> "+"
 		| OpAdd -> "+"
@@ -1128,7 +1129,7 @@ module Printer = struct
 					if !had_kw_args then abort "Arguments after KwArgs are not allowed" p;
 					if !had_kw_args then abort "Arguments after KwArgs are not allowed" p;
 					had_kw_args := true;
 					had_kw_args := true;
 					"**" ^ name
 					"**" ^ name
-				| TAbstract({a_path = ["python"],"VarArgs"},_) ->
+				| TAbstract({a_path = (["python"],"VarArgs" | ["haxe"],"Rest")},_) ->
 					check_err ();
 					check_err ();
 					had_var_args := true;
 					had_var_args := true;
 					"*" ^ name
 					"*" ^ name

+ 96 - 13
src/generators/genswf9.ml

@@ -86,6 +86,22 @@ type context = {
 	boot : path;
 	boot : path;
 	swf_protected : bool;
 	swf_protected : bool;
 	need_ctor_skip : bool;
 	need_ctor_skip : bool;
+	(*
+		Takes an argument list of `call(arg1, arg2, ...)` and if there is a trailing argument `...rest` then returns
+		```
+		if(rest.length == 0) call(arg1, arg2)
+		else if(rest.length == 1) call(arg1, arg2, rest[0])
+		else if(rest.length == 2) call(arg1, arg2, rest[0], rest[1])
+		<... up to 20 rest args ...>
+		else throw "Too many rest arguments"
+		```
+		otherwise returns `None`
+	*)
+	handle_spread_args : basic_types ->
+						texpr list (* arguments *) ->
+						t (* call result type *) ->
+						(texpr list -> texpr) (* a function which takes an argument list and should return a call expression *) ->
+						texpr option;
 	mutable cur_class : tclass;
 	mutable cur_class : tclass;
 	mutable debug : bool;
 	mutable debug : bool;
 	mutable last_line : int;
 	mutable last_line : int;
@@ -762,12 +778,13 @@ let begin_fun ctx args tret el stat p =
 		| None -> if c <> None then dparams := Some [v]
 		| None -> if c <> None then dparams := Some [v]
 		| Some l -> dparams := Some (v :: l)
 		| Some l -> dparams := Some (v :: l)
 	in
 	in
-
 	let args, varargs = (match List.rev args with
 	let args, varargs = (match List.rev args with
 		| (({ v_name = "__arguments__"; v_type = t } as v),_) :: l ->
 		| (({ v_name = "__arguments__"; v_type = t } as v),_) :: l ->
 			(match follow t with
 			(match follow t with
 			| TInst ({ cl_path = ([],"Array") },_) -> List.rev l, Some (v,true)
 			| TInst ({ cl_path = ([],"Array") },_) -> List.rev l, Some (v,true)
 			| _ -> List.rev l, Some(v,false))
 			| _ -> List.rev l, Some(v,false))
+		| (v,_) :: l when ExtType.is_rest (Type.follow v.v_type) ->
+			List.rev l, Some (v,true)
 		| _ ->
 		| _ ->
 			args, None
 			args, None
 	) in
 	) in
@@ -1112,7 +1129,12 @@ let rec gen_expr_content ctx retval e =
 	| TBinop (op,e1,e2) ->
 	| TBinop (op,e1,e2) ->
 		gen_binop ctx retval op e1 e2 e.etype e.epos
 		gen_binop ctx retval op e1 e2 e.etype e.epos
 	| TCall (f,el) ->
 	| TCall (f,el) ->
-		gen_call ctx retval f el e.etype
+		(match ctx.handle_spread_args ctx.com.basic el e.etype (fun el -> { e with eexpr = TCall(f,el) }) with
+		| Some e ->
+			gen_expr ctx retval e
+		| None ->
+			gen_call ctx retval f el e.etype
+		)
 	| TNew ({ cl_path = [],"Array" },_,[]) ->
 	| TNew ({ cl_path = [],"Array" },_,[]) ->
 		(* it seems that [] is 4 time faster than new Array() *)
 		(* it seems that [] is 4 time faster than new Array() *)
 		write ctx (HArray 0)
 		write ctx (HArray 0)
@@ -1121,17 +1143,22 @@ let rec gen_expr_content ctx retval e =
 		write ctx HThrow;
 		write ctx HThrow;
 		no_value ctx retval
 		no_value ctx retval
 	| TNew (c,tl,pl) ->
 	| TNew (c,tl,pl) ->
-		let id = type_id ctx (TInst (c,tl)) in
-		(match id with
-		| HMParams _ ->
-			gen_type ctx id;
-			List.iter (gen_expr ctx true) pl;
-			write ctx (HConstruct (List.length pl))
-		| _ ->
-			write ctx (HFindPropStrict id);
-			List.iter (gen_expr ctx true) pl;
-			write ctx (HConstructProperty (id,List.length pl))
-		);
+		(match ctx.handle_spread_args ctx.com.basic pl e.etype (fun pl -> { e with eexpr = TNew(c,tl,pl) }) with
+		| Some e ->
+			gen_expr ctx retval e
+		| None ->
+			let id = type_id ctx (TInst (c,tl)) in
+			(match id with
+			| HMParams _ ->
+				gen_type ctx id;
+				List.iter (gen_expr ctx true) pl;
+				write ctx (HConstruct (List.length pl))
+			| _ ->
+				write ctx (HFindPropStrict id);
+				List.iter (gen_expr ctx true) pl;
+				write ctx (HConstructProperty (id,List.length pl))
+			);
+		)
 	| TFunction f ->
 	| TFunction f ->
 		write ctx (HFunction (generate_function ctx f true))
 		write ctx (HFunction (generate_function ctx f true))
 	| TIf (e0,e1,e2) ->
 	| TIf (e0,e1,e2) ->
@@ -1399,8 +1426,27 @@ let rec gen_expr_content ctx retval e =
 		end
 		end
 	| TIdent s ->
 	| TIdent s ->
 		abort ("Unbound variable " ^ s) e.epos
 		abort ("Unbound variable " ^ s) e.epos
+and args_as_array ctx mandatory_args spread_arg =
+	match mandatory_args with
+	| [] ->
+		spread_arg
+	| _ ->
+		let p = punion_el (List.map (fun e -> ((),e.epos)) mandatory_args) in
+		let array = mk (TArrayDecl mandatory_args) (ctx.com.basic.tarray t_dynamic) p in
+		let concat = mk (TField (array,FDynamic "concat")) t_dynamic spread_arg.epos in
+		mk (TCall (concat,[spread_arg])) (ctx.com.basic.tarray t_dynamic) (punion p spread_arg.epos)
 
 
 and gen_call ctx retval e el r =
 and gen_call ctx retval e el r =
+	match List.rev el with
+	(* generate a call with `...rest` as `callee.apply(null, [param1, param2].concat(rest))` *)
+	| { eexpr = TUnop (Spread, Prefix, rest) } :: el_rev ->
+		let null = mk (TConst TNull) t_dynamic null_pos
+		and t_array_dyn = ctx.com.basic.tarray t_dynamic in
+		let t = TFun (["thisArg",false,t_dynamic; "argArray",false,t_array_dyn],r) in
+		let apply = mk (TField (e,FDynamic "apply")) t e.epos in
+		gen_call ctx retval apply [null; args_as_array ctx (List.rev el_rev) rest] r
+	(* normal call without `...rest` *)
+	| _ ->
 	match e.eexpr , el with
 	match e.eexpr , el with
 	| TIdent "__is__", [e;t] ->
 	| TIdent "__is__", [e;t] ->
 		gen_expr ctx true e;
 		gen_expr ctx true e;
@@ -1636,6 +1682,8 @@ and gen_unop ctx retval op flag e =
 	| NegBits ->
 	| NegBits ->
 		gen_expr ctx true e;
 		gen_expr ctx true e;
 		write ctx (HOp A3OBitNot);
 		write ctx (HOp A3OBitNot);
+	| Spread ->
+		die ~p:e.epos "Unhandled spread operator" __LOC__
 	| Increment
 	| Increment
 	| Decrement ->
 	| Decrement ->
 		let incr = (op = Increment) in
 		let incr = (op = Increment) in
@@ -2785,6 +2833,41 @@ let generate com boot_name =
 	let ctx = {
 	let ctx = {
 		com = com;
 		com = com;
 		need_ctor_skip = Common.has_feature com "Type.createEmptyInstance";
 		need_ctor_skip = Common.has_feature com "Type.createEmptyInstance";
+		handle_spread_args = (fun basic args t_result args_to_expr ->
+			match List.rev args with
+			| { eexpr = TUnop (Spread,Prefix,rest) } :: args_rev ->
+				let t_rest_item = match Type.follow rest.etype with TAbstract (_,[t]) -> t | _ -> die "" __LOC__ in
+				let t_array_dyn = basic.tarray t_dynamic in
+				let c_array = match t_array_dyn with TInst (c,_) -> c | _ -> die "" __LOC__ in
+				let length =
+					let faccess =
+						try
+							let cf = PMap.find "length" c_array.cl_fields in
+							FInstance (c_array,[t_dynamic],cf)
+						with Not_found ->
+							FDynamic "length"
+					in
+					mk (TField (rest,faccess)) basic.tint rest.epos
+				in
+				let const n = mk (TConst (TInt (Int32.of_int n))) basic.tint rest.epos in
+				let check n =
+					mk (TBinop (OpEq, length, const n)) basic.tbool rest.epos
+				in
+				let rec nargs n acc =
+					if n < 0 then acc
+					else nargs (n - 1) ((mk (TArray (rest, const n)) t_rest_item rest.epos) :: acc)
+				in
+				let rec loop n e_else =
+					let args = (nargs (n - 1) args_rev) in
+					let e = mk (TIf (check n, args_to_expr args, Some e_else)) t_result rest.epos in
+					if n = 0 then e
+					else loop (n - 1) e
+				in
+				let msg = mk (TConst (TString "Too many rest arguments")) basic.tstring rest.epos in
+				Some (loop 20 (mk (TThrow msg) t_dynamic rest.epos))
+			| _ ->
+				None
+		);
 		debug = com.Common.debug;
 		debug = com.Common.debug;
 		cur_class = null_class;
 		cur_class = null_class;
 		boot = ([],boot_name);
 		boot = ([],boot_name);

+ 1 - 1
src/macro/eval/evalDebugMisc.ml

@@ -285,7 +285,7 @@ let rec expr_to_value ctx env e =
 				end
 				end
 			| NegBits ->
 			| NegBits ->
 				op_sub (pos e) (vint32 (Int32.minus_one)) (loop e1)
 				op_sub (pos e) (vint32 (Int32.minus_one)) (loop e1)
-			| Increment | Decrement ->
+			| Increment | Decrement | Spread ->
 				raise Exit
 				raise Exit
 			end
 			end
 		| ECall(e1,el) ->
 		| ECall(e1,el) ->

+ 4 - 0
src/macro/eval/evalJit.ml

@@ -172,6 +172,10 @@ and unop jit op flag e1 p =
 		end
 		end
 	| Decrement ->
 	| Decrement ->
 		op_decr jit e1 (flag = Prefix) p
 		op_decr jit e1 (flag = Prefix) p
+	| Spread ->
+		match flag with
+		| Postfix -> die ~p:p "Postfix spread operator is not supported" __LOC__
+		| Prefix -> jit_expr jit false e1
 
 
 and jit_default jit return def =
 and jit_default jit return def =
 	match def with
 	match def with

+ 2 - 0
src/macro/macroApi.ml

@@ -270,6 +270,7 @@ let encode_unop op =
 	| Not -> 2
 	| Not -> 2
 	| Neg -> 3
 	| Neg -> 3
 	| NegBits -> 4
 	| NegBits -> 4
+	| Spread -> 5
 	in
 	in
 	encode_enum IUnop tag []
 	encode_enum IUnop tag []
 
 
@@ -591,6 +592,7 @@ let decode_unop op =
 	| 2, [] -> Not
 	| 2, [] -> Not
 	| 3, [] -> Neg
 	| 3, [] -> Neg
 	| 4, [] -> NegBits
 	| 4, [] -> NegBits
+	| 5, [] -> Spread
 	| _ -> raise Invalid_expr
 	| _ -> raise Invalid_expr
 
 
 let decode_import_mode t =
 let decode_import_mode t =

+ 36 - 0
src/optimization/inline.ml

@@ -650,6 +650,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f
 		| None -> raise Exit
 		| None -> raise Exit
 		| Some e -> Some e)
 		| Some e -> Some e)
 	with Exit ->
 	with Exit ->
+	let params = inline_rest_params ctx f params p in
 	let state = new inline_state ctx ethis params cf f p in
 	let state = new inline_state ctx ethis params cf f p in
 	let vthis_opt = state#initialize in
 	let vthis_opt = state#initialize in
 	let opt f = function
 	let opt f = function
@@ -882,3 +883,38 @@ and type_inline_ctor ctx c cf tf ethis el po =
 			{tf with tf_expr = mk (TBlock (field_inits @ bl)) ctx.t.tvoid c.cl_pos}
 			{tf with tf_expr = mk (TBlock (field_inits @ bl)) ctx.t.tvoid c.cl_pos}
 	in
 	in
 	type_inline ctx cf tf ethis el ctx.t.tvoid None po true
 	type_inline ctx cf tf ethis el ctx.t.tvoid None po true
+
+and inline_rest_params ctx f params p =
+	if not ctx.com.config.pf_supports_rest_args then
+		params
+	else
+		let rec loop args params =
+			match args, params with
+			(* last argument expects rest parameters *)
+			| [(v,_)], params when ExtType.is_rest (follow v.v_type) ->
+				(match params with
+				(* In case of `...rest` just use `rest` *)
+				| [{ eexpr = TUnop(Spread,Prefix,e) }] -> [e]
+				(* In other cases: `haxe.Rest.of([param1, param2, ...])` *)
+				| _ ->
+					match follow v.v_type with
+					| TAbstract ({ a_path = ["haxe"],"Rest"; a_impl = Some c } as a, [t]) as rest_t ->
+						let cf =
+							try PMap.find "of" c.cl_statics
+							with Not_found -> die ~p:c.cl_name_pos "Can't find haxe.Rest.of function" __LOC__
+						and p = punion_el (List.map (fun e -> (),e.epos) params) in
+						(* [param1, param2, ...] *)
+						let array = mk (TArrayDecl params) (ctx.t.tarray t) p in
+						(* haxe.Rest.of(array) *)
+						[make_static_call ctx c cf (apply_params a.a_params [t]) [array] rest_t p]
+					| _ ->
+						die ~p:v.v_pos "Unexpected rest arguments type" __LOC__
+				)
+			| a :: args, e :: params ->
+				e :: loop args params
+			| [], params ->
+				params
+			| _ :: _, [] ->
+				[]
+		in
+		loop f.tf_args params

+ 13 - 1
src/syntax/grammar.mly

@@ -668,6 +668,13 @@ and parse_complex_type_inner allow_named = parser
 		| [< >] -> serror())
 		| [< >] -> serror())
 	| [< '(Question,p1); t,p2 = parse_complex_type_inner allow_named >] ->
 	| [< '(Question,p1); t,p2 = parse_complex_type_inner allow_named >] ->
 		CTOptional (t,p2),punion p1 p2
 		CTOptional (t,p2),punion p1 p2
+	| [< '(Spread,p1); t,p2 = parse_complex_type_inner allow_named >] ->
+		let hint =
+			match t with
+			| CTNamed (_,hint) -> hint
+			| _ -> (t,p2)
+		in
+		CTPath (mk_type_path ~params:[TPType hint] (["haxe"],"Rest")),punion p1 p2
 	| [< n = dollar_ident; s >] ->
 	| [< n = dollar_ident; s >] ->
 		(match s with parser
 		(match s with parser
 		| [< '(DblDot,_) when allow_named; t = parse_complex_type >] ->
 		| [< '(DblDot,_) when allow_named; t = parse_complex_type >] ->
@@ -991,6 +998,9 @@ and parse_fun_param s =
 	match s with parser
 	match s with parser
 	| [< '(Question,_); name, pn = dollar_ident; t = popt parse_type_hint; c = parse_fun_param_value >] -> ((name,pn),true,meta,t,c)
 	| [< '(Question,_); name, pn = dollar_ident; t = popt parse_type_hint; c = parse_fun_param_value >] -> ((name,pn),true,meta,t,c)
 	| [< name, pn = dollar_ident; t = popt parse_type_hint; c = parse_fun_param_value >] -> ((name,pn),false,meta,t,c)
 	| [< name, pn = dollar_ident; t = popt parse_type_hint; c = parse_fun_param_value >] -> ((name,pn),false,meta,t,c)
+	| [< '(Spread,_); name, pn = dollar_ident; t = parse_type_hint; c = parse_fun_param_value >] ->
+		let t = CTPath (mk_type_path ~params:[TPType t] (["haxe"],"Rest")), snd t in
+		((name,pn),false,meta,Some t,c)
 
 
 and parse_fun_param_value = parser
 and parse_fun_param_value = parser
 	| [< '(Binop OpAssign,_); e = expr >] -> Some e
 	| [< '(Binop OpAssign,_); e = expr >] -> Some e
@@ -1361,7 +1371,8 @@ and expr = parser
 		)
 		)
 	| [< '(BkOpen,p1); e = parse_array_decl p1; s >] -> expr_next e s
 	| [< '(BkOpen,p1); e = parse_array_decl p1; s >] -> expr_next e s
 	| [< '(Kwd Function,p1); e = parse_function p1 false; >] -> e
 	| [< '(Kwd Function,p1); e = parse_function p1 false; >] -> e
-	| [< '(Unop op,p1) when is_prefix op; e = expr >] -> make_unop op e p1
+	| [< '(Unop op,p1); e = expr >] -> make_unop op e p1
+	| [< '(Spread,p1); e = expr >] -> make_unop Spread e (punion p1 (pos e))
 	| [< '(Binop OpSub,p1); e = expr >] ->
 	| [< '(Binop OpSub,p1); e = expr >] ->
 		make_unop Neg e p1
 		make_unop Neg e p1
 	(*/* removed unary + : this cause too much syntax errors go unnoticed, such as "a + + 1" (missing 'b')
 	(*/* removed unary + : this cause too much syntax errors go unnoticed, such as "a + + 1" (missing 'b')
@@ -1481,6 +1492,7 @@ and expr_next' e1 = parser
 		| [< e2 = secure_expr >] ->
 		| [< e2 = secure_expr >] ->
 			make_binop OpGt e1 e2)
 			make_binop OpGt e1 e2)
 	| [< '(Binop op,_); e2 = secure_expr >] -> make_binop op e1 e2
 	| [< '(Binop op,_); e2 = secure_expr >] -> make_binop op e1 e2
+	| [< '(Spread,_); e2 = secure_expr >] -> make_binop OpInterval e1 e2
 	| [< '(Unop op,p) when is_postfix e1 op; s >] ->
 	| [< '(Unop op,p) when is_postfix e1 op; s >] ->
 		expr_next (EUnop (op,Postfix,e1), punion (pos e1) p) s
 		expr_next (EUnop (op,Postfix,e1), punion (pos e1) p) s
 	| [< '(Question,_); e2 = expr; s >] ->
 	| [< '(Question,_); e2 = expr; s >] ->

+ 1 - 1
src/syntax/lexer.ml

@@ -355,7 +355,7 @@ let rec token lexbuf =
 	| "||" -> mk lexbuf (Binop OpBoolOr)
 	| "||" -> mk lexbuf (Binop OpBoolOr)
 	| "<<" -> mk lexbuf (Binop OpShl)
 	| "<<" -> mk lexbuf (Binop OpShl)
 	| "->" -> mk lexbuf Arrow
 	| "->" -> mk lexbuf Arrow
-	| "..." -> mk lexbuf (Binop OpInterval)
+	| "..." -> mk lexbuf Spread
 	| "=>" -> mk lexbuf (Binop OpArrow)
 	| "=>" -> mk lexbuf (Binop OpArrow)
 	| "!" -> mk lexbuf (Unop Not)
 	| "!" -> mk lexbuf (Unop Not)
 	| "<" -> mk lexbuf (Binop OpLt)
 	| "<" -> mk lexbuf (Binop OpLt)

+ 1 - 0
src/syntax/reification.ml

@@ -277,6 +277,7 @@ let reify in_macro =
 				| Not -> "OpNot"
 				| Not -> "OpNot"
 				| Neg -> "OpNeg"
 				| Neg -> "OpNeg"
 				| NegBits -> "OpNegBits"
 				| NegBits -> "OpNegBits"
+				| Spread -> "OpSpread"
 			) [] p in
 			) [] p in
 			expr "EUnop" [op;to_bool (flag = Postfix) p;loop e]
 			expr "EUnop" [op;to_bool (flag = Postfix) p;loop e]
 		| EVars vl ->
 		| EVars vl ->

+ 44 - 3
src/typing/callUnification.ml

@@ -95,10 +95,51 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload =
 				| name :: _ -> call_error (Cannot_skip_non_nullable name) callp;
 				| name :: _ -> call_error (Cannot_skip_non_nullable name) callp;
 			end;
 			end;
 			[]
 			[]
-		| _,[name,false,t] when (match follow t with TAbstract({a_path = ["haxe";"extern"],"Rest"},_) -> true | _ -> false) ->
+		| _,[name,false,TAbstract({ a_path = ["cpp"],"Rest" },[t])] ->
+			(try List.map (fun e -> type_against name t e) el
+			with WithTypeError(ul,p) -> arg_error ul name false p)
+		| _,[name,false,t] when ExtType.is_rest (follow t) ->
 			begin match follow t with
 			begin match follow t with
-				| TAbstract({a_path=(["haxe";"extern"],"Rest")},[t]) ->
-					(try List.map (fun e -> type_against name t e) el with WithTypeError(ul,p) -> arg_error ul name false p)
+				| TAbstract({a_path=(["haxe"],"Rest")},[arg_t]) ->
+					let unexpected_spread p =
+						arg_error (Custom "Cannot spread arguments with additional rest arguments") name false p
+					in
+					(* these platforms deal with rest args on their own *)
+					if ctx.com.config.pf_supports_rest_args then
+						match el with
+						| [(EUnop (Spread,Prefix,e),p)] ->
+							(try [mk (TUnop (Spread, Prefix, type_against name t e)) t p]
+							with WithTypeError(ul,p) -> arg_error ul name false p)
+						| _ ->
+							(try
+								List.map (fun e ->
+									match e with
+									| (EUnop (Spread,Prefix,_),p) ->
+										unexpected_spread p
+									| _ -> type_against name arg_t e
+								) el
+							with WithTypeError(ul,p) ->
+								arg_error ul name false p)
+					(* for other platforms make sure rest arguments are wrapped in an array *)
+					else begin
+						match el with
+						| [(EUnop (Spread,Prefix,e),p)] ->
+							(try [type_against name t e]
+							with WithTypeError(ul,p) -> arg_error ul name false p)
+						| [] ->
+							(try [type_against name t (EArrayDecl [],callp)]
+							with WithTypeError(ul,p) -> arg_error ul name false p)
+						| (_,p1) :: _ ->
+							let p =
+								List.fold_left (fun p (e1,p2) ->
+									match e1 with
+									| EUnop (Spread,Prefix,_) -> unexpected_spread p2
+									| _ -> punion p p2
+								) p1 el
+							in
+							(try [type_against name t (EArrayDecl el,p)]
+							with WithTypeError(ul,p) -> arg_error ul name false p)
+					end
 				| _ ->
 				| _ ->
 					die "" __LOC__
 					die "" __LOC__
 			end
 			end

+ 5 - 8
src/typing/functionArguments.ml

@@ -90,14 +90,11 @@ object(self)
 			l
 			l
 
 
 	method private check_rest (is_last : bool) (eo : expr option) (opt : bool) (t : Type.t) (pn : pos) =
 	method private check_rest (is_last : bool) (eo : expr option) (opt : bool) (t : Type.t) (pn : pos) =
-		match follow t with
-			| TAbstract({a_path = ["haxe";"extern"],"Rest"},_) ->
-				if not is_extern then error "Rest argument are only supported for extern methods" pn;
-				if opt then error "Rest argument cannot be optional" pn;
-				begin match eo with None -> () | Some (_,p) -> error "Rest argument cannot have default value" p end;
-				if not is_last then error "Rest should only be used for the last function argument" pn;
-			| _ ->
-				()
+		if ExtType.is_rest (follow t) then begin
+			if opt then error "Rest argument cannot be optional" pn;
+			begin match eo with None -> () | Some (_,p) -> error "Rest argument cannot have default value" p end;
+			if not is_last then error "Rest should only be used for the last function argument" pn;
+		end
 
 
 	(* Returns the `(tvar * texpr option) list` for `tf_args`. Also checks the validity of argument names and whether or not
 	(* Returns the `(tvar * texpr option) list` for `tf_args`. Also checks the validity of argument names and whether or not
 	   an argument should be displayed. *)
 	   an argument should be displayed. *)

+ 7 - 0
src/typing/operators.ml

@@ -801,6 +801,9 @@ let type_unop ctx op flag e with_type p =
 		| _ ->
 		| _ ->
 			raise Not_found
 			raise Not_found
 	in
 	in
+	let unexpected_spread p =
+		error "Spread unary operator is only allowed for unpacking the last argument in a call with rest arguments" p
+	in
 	let make e =
 	let make e =
 		let check_int () =
 		let check_int () =
 			match classify e.etype with
 			match classify e.etype with
@@ -825,6 +828,8 @@ let type_unop ctx op flag e with_type p =
 				check_int()
 				check_int()
 			| Neg ->
 			| Neg ->
 				check_int()
 				check_int()
+			| Spread ->
+				unexpected_spread p
 		in
 		in
 		mk (TUnop (op,flag,e)) t p
 		mk (TUnop (op,flag,e)) t p
 	in
 	in
@@ -835,6 +840,8 @@ let type_unop ctx op flag e with_type p =
 			make e
 			make e
 	in
 	in
 	match op with
 	match op with
+	| Spread ->
+		unexpected_spread p
 	| Not | Neg | NegBits ->
 	| Not | Neg | NegBits ->
 		let access_get = !type_access_ref ctx (fst e) (snd e) MGet WithType.value (* WITHTYPETODO *) in
 		let access_get = !type_access_ref ctx (fst e) (snd e) MGet WithType.value (* WITHTYPETODO *) in
 		let e = acc_get ctx access_get p in
 		let e = acc_get ctx access_get p in

+ 13 - 10
src/typing/overloadResolution.ml

@@ -13,16 +13,19 @@ let unify_cf map_type c cf el =
 						Type.unify e.etype t;
 						Type.unify e.etype t;
 						loop2 (e :: acc) el tl
 						loop2 (e :: acc) el tl
 					with _ ->
 					with _ ->
-						match t,tl with
-						| TAbstract({a_path=["haxe";"extern"],"Rest"},[t]),[] ->
-							begin try
-								let el = List.map (fun e -> unify t e.etype; e) el in
-								let fcc = make_field_call_candidate ((List.rev acc) @ el) ret monos tf cf (c,cf,monos) in
-								Some fcc
-							with _ ->
-								None
-							end
-						| _ ->
+						if Type.ExtType.is_rest (follow t) then
+							match follow t,tl with
+							| TAbstract({a_path=["haxe"],"Rest"},[t]),[] ->
+								begin try
+									let el = List.map (fun e -> unify t e.etype; e) el in
+									let fcc = make_field_call_candidate ((List.rev acc) @ el) ret monos tf cf (c,cf,monos) in
+									Some fcc
+								with _ ->
+									None
+								end
+							| _ ->
+								Globals.die "" __LOC__
+						else
 							None
 							None
 					end
 					end
 				| [],[] ->
 				| [],[] ->

+ 25 - 0
std/cpp/Rest.hx

@@ -0,0 +1,25 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package cpp;
+
+abstract Rest<T>(Array<T>) {}

+ 0 - 2
std/cpp/Stdio.hx

@@ -22,8 +22,6 @@
 
 
 package cpp;
 package cpp;
 
 
-import haxe.extern.Rest;
-
 @:include("stdio.h")
 @:include("stdio.h")
 extern class Stdio {
 extern class Stdio {
 	@:native("printf")
 	@:native("printf")

+ 50 - 0
std/cs/_std/haxe/Rest.hx

@@ -0,0 +1,50 @@
+package haxe;
+
+import haxe.iterators.RestIterator;
+import haxe.iterators.RestKeyValueIterator;
+import cs.NativeArray;
+import cs.system.Array as CsArray;
+
+private typedef NativeRest<T> = #if erase_generics NativeArray<Dynamic> #else NativeArray<T> #end;
+
+@:coreApi
+abstract Rest<T>(NativeRest<T>) {
+	public var length(get,never):Int;
+	inline function get_length():Int
+		return this.Length;
+
+	@:from static public inline function of<T>(array:Array<T>):Rest<T>
+		return new Rest(@:privateAccess array.__a);
+
+	inline function new(a:NativeRest<T>):Void
+		this = a;
+
+	@:arrayAccess inline function get(index:Int):T
+		return this[index];
+
+	@:to public function toArray():Array<T> {
+		var result = new NativeRest(this.Length);
+		CsArray.Copy(this, 0, result, 0, this.Length);
+		return @:privateAccess Array.ofNative(result);
+	}
+
+	public inline function iterator():RestIterator<T>
+		return new RestIterator<T>(this);
+
+	public inline function keyValueIterator():RestKeyValueIterator<T>
+		return new RestKeyValueIterator<T>(this);
+
+	public function append(item:T):Rest<T> {
+		var result = new NativeRest(this.Length + 1);
+		CsArray.Copy(this, 0, result, 0, this.Length);
+		result[this.Length] = item;
+		return new Rest(result);
+	}
+
+	public function prepend(item:T):Rest<T> {
+		var result = new NativeRest(this.Length + 1);
+		CsArray.Copy(this, 0, result, 1, this.Length);
+		result[0] = item;
+		return new Rest(result);
+	}
+}

+ 70 - 0
std/haxe/Rest.hx

@@ -0,0 +1,70 @@
+package haxe;
+
+import haxe.iterators.RestIterator;
+import haxe.iterators.RestKeyValueIterator;
+
+private typedef NativeRest<T> = Array<T>;
+
+/**
+	A special type that represents "rest" function argument.
+
+	Should be used as a type for the last argument of a method, representing that
+	arbitrary number of arguments of given type can be passed to that method.
+
+	Allows to use array access by index to get values of rest arguments.
+	If the index exceeds the amount of rest arguments passed, the result is unspecified.
+**/
+@:coreApi
+abstract Rest<T>(NativeRest<T>) {
+	/** Amount of arguments passed as rest arguments */
+	public var length(get,never):Int;
+	inline function get_length():Int
+		return this.length;
+
+	/**
+		Create rest arguments using contents of `array`.
+
+		WARNING:
+		Depending on a target platform modifying `array` after using this method
+		may affect the created `Rest` instance.
+		Use `Rest.of(array.copy())` to avoid that.
+	**/
+	@:from static public inline function of<T>(array:Array<T>):Rest<T>
+		return new Rest(array);
+
+	inline function new(array:Array<T>):Void
+		this = array;
+
+	@:arrayAccess inline function get(index:Int):T
+		return this[index];
+
+	/**
+		Creates an array containing all the values of rest arguments.
+	**/
+	@:to public #if !cppia inline #end function toArray():Array<T>
+		return this.copy();
+
+	public inline function iterator():RestIterator<T>
+		return new RestIterator<T>(this);
+
+	public inline function keyValueIterator():RestKeyValueIterator<T>
+		return new RestKeyValueIterator<T>(this);
+
+	/**
+		Create a new rest arguments collection by appending `item` to this one.
+	**/
+	public function append(item:T):Rest<T> {
+		var result = this.copy();
+		result.push(item);
+		return new Rest(result);
+	}
+
+	/**
+		Create a new rest arguments collection by prepending this one with `item`.
+	**/
+	public function prepend(item:T):Rest<T> {
+		var result = this.copy();
+		result.unshift(item);
+		return new Rest(result);
+	}
+}

+ 4 - 4
std/haxe/extern/Rest.hx

@@ -23,12 +23,12 @@
 package haxe.extern;
 package haxe.extern;
 
 
 /**
 /**
-	A special abstract type that represents "rest" function argument.
-
+	DEPRECATED: use haxe.Rest instead.
+	
+	A special type that represents "rest" function argument.
 	Should be used as a type for the last argument of an extern method,
 	Should be used as a type for the last argument of an extern method,
 	representing that arbitrary number of arguments of given type can be
 	representing that arbitrary number of arguments of given type can be
 	passed to that method.
 	passed to that method.
-
 	@see <https://haxe.org/manual/lf-externs.html>
 	@see <https://haxe.org/manual/lf-externs.html>
 **/
 **/
-abstract Rest<T>(Array<T>) {}
+typedef Rest<T> = haxe.Rest<T>

+ 19 - 0
std/haxe/iterators/RestIterator.hx

@@ -0,0 +1,19 @@
+package haxe.iterators;
+
+class RestIterator<T> {
+	final args:Rest<T>;
+	var current:Int = 0;
+
+	@:allow(haxe.Rest)
+	inline function new(args:Any) {
+		this.args = args;
+	}
+
+	public inline function hasNext():Bool {
+		return current < args.length;
+	}
+
+	public inline function next():T {
+		return args[current++];
+	}
+}

+ 19 - 0
std/haxe/iterators/RestKeyValueIterator.hx

@@ -0,0 +1,19 @@
+package haxe.iterators;
+
+class RestKeyValueIterator<T> {
+	final args:Rest<T>;
+	var current:Int = 0;
+
+	@:allow(haxe.Rest)
+	inline function new(args:Any) {
+		this.args = args;
+	}
+
+	public inline function hasNext():Bool {
+		return current < args.length;
+	}
+
+	public inline function next():{key:Int, value:T} {
+		return {key:current, value:args[current++]};
+	}
+}

+ 5 - 0
std/haxe/macro/Expr.hx

@@ -245,6 +245,11 @@ enum Unop {
 		`~`
 		`~`
 	**/
 	**/
 	OpNegBits;
 	OpNegBits;
+
+	/**
+		`...`
+	**/
+	OpSpread;
 }
 }
 
 
 /**
 /**

+ 1 - 0
std/haxe/macro/Printer.hx

@@ -47,6 +47,7 @@ class Printer {
 			case OpNot: "!";
 			case OpNot: "!";
 			case OpNeg: "-";
 			case OpNeg: "-";
 			case OpNegBits: "~";
 			case OpNegBits: "~";
+			case OpSpread: "...";
 		}
 		}
 
 
 	public function printBinop(op:Binop)
 	public function printBinop(op:Binop)

+ 60 - 0
std/java/_std/haxe/Rest.hx

@@ -0,0 +1,60 @@
+package haxe;
+
+import haxe.iterators.RestIterator;
+import haxe.iterators.RestKeyValueIterator;
+import java.NativeArray;
+import java.lang.System;
+import java.lang.Object;
+import java.util.Arrays;
+
+private typedef NativeRest<T> = NativeArray<Object>;
+
+@:coreApi
+abstract Rest<T>(NativeRest<T>) {
+	public var length(get,never):Int;
+	inline function get_length():Int
+		return this.length;
+
+	@:from static public function of<T>(array:Array<T>):Rest<T> {
+		var native = @:privateAccess array.__a;
+		var result:NativeRest<T>;
+		#if jvm
+			result = (cast native:Object).clone();
+		#else
+			result = new NativeRest<T>(native.length);
+			for(i in 0...native.length)
+				result[i] = cast native[i];
+		#end
+		return new Rest(result);
+	}
+
+	inline function new(a:NativeRest<T>):Void
+		this = a;
+
+	@:arrayAccess inline function get(index:Int):T
+		return cast this[index];
+
+	@:to public function toArray():Array<T> {
+		return [for(i in 0...this.length) cast this[i]];
+	}
+
+	public inline function iterator():RestIterator<T>
+		return new RestIterator<T>(this);
+
+	public inline function keyValueIterator():RestKeyValueIterator<T>
+		return new RestKeyValueIterator<T>(this);
+
+	public function append(item:T):Rest<T> {
+		var result = new NativeRest<T>(this.length + 1);
+		System.arraycopy(this, 0, result, 0, this.length);
+		result[this.length] = cast item;
+		return new Rest(result);
+	}
+
+	public function prepend(item:T):Rest<T> {
+		var result = new NativeRest<T>(this.length + 1);
+		System.arraycopy(this, 0, result, 1, this.length);
+		result[0] = cast item;
+		return new Rest(result);
+	}
+}

+ 51 - 0
std/lua/_std/haxe/Rest.hx

@@ -0,0 +1,51 @@
+package haxe;
+
+import lua.Lua.select;
+import lua.Table;
+import lua.PairTools.copy;
+import lua.TableTools.maxn;
+import lua.TableTools.pack;
+import lua.TableTools.unpack;
+import haxe.iterators.RestIterator;
+import haxe.iterators.RestKeyValueIterator;
+
+private typedef NativeRest<T> = Table<Int,T>;
+
+@:coreApi
+abstract Rest<T>(NativeRest<T>) {
+	public var length(get, never):Int;
+	inline function get_length():Int
+		return maxn(this);
+
+	@:from static public function of<T>(array:Array<T>):Rest<T> {
+		return new Rest(Table.fromArray(array));
+	}
+
+	inline function new(table:Table<Int,T>):Void
+		this = table;
+
+	@:arrayAccess inline function get(index:Int):T
+		return this[index + 1];
+
+	@:to public function toArray():Array<T> {
+		return Table.toArray(this);
+	}
+
+	public inline function iterator():RestIterator<T>
+		return new RestIterator<T>(this);
+
+	public inline function keyValueIterator():RestKeyValueIterator<T>
+		return new RestKeyValueIterator<T>(this);
+
+	public inline function append(item:T):Rest<T> {
+		var result = copy(this);
+		Table.insert(result, item);
+		return new Rest(result);
+	}
+
+	public inline function prepend(item:T):Rest<T> {
+		var result = copy(this);
+		Table.insert(result, 1, item);
+		return new Rest(result);
+	}
+}

+ 1 - 1
std/php/Syntax.hx

@@ -22,7 +22,7 @@
 
 
 package php;
 package php;
 
 
-import haxe.extern.Rest;
+import haxe.Rest;
 import haxe.extern.AsVar;
 import haxe.extern.AsVar;
 import haxe.extern.EitherType;
 import haxe.extern.EitherType;
 
 

+ 46 - 0
std/php/_std/haxe/Rest.hx

@@ -0,0 +1,46 @@
+package haxe;
+
+import php.*;
+import haxe.iterators.RestIterator;
+import haxe.iterators.RestKeyValueIterator;
+
+private typedef NativeRest<T> = NativeIndexedArray<T>;
+
+@:coreApi
+@:semantics(value)
+abstract Rest<T>(NativeRest<T>) {
+	public var length(get,never):Int;
+	inline function get_length():Int
+		return Global.count(this);
+
+	@:from
+	static public inline function of<T>(array:Array<T>):Rest<T>
+		return new Rest(@:privateAccess array.arr);
+
+	inline function new(a:NativeIndexedArray<T>):Void
+		this = a;
+
+	@:arrayAccess inline function get(index:Int):T
+		return this[index];
+
+	@:to public inline function toArray():Array<T>
+		return @:privateAccess Array.wrap(this);
+
+	public inline function iterator():RestIterator<T>
+		return new RestIterator<T>(this);
+
+	public inline function keyValueIterator():RestKeyValueIterator<T>
+		return new RestKeyValueIterator<T>(this);
+
+	public inline function append(item:T):Rest<T> {
+		var result = this;
+		result.push(item);
+		return new Rest(result);
+	}
+
+	public inline function prepend(item:T):Rest<T> {
+		var result = this;
+		Global.array_unshift(result, item);
+		return new Rest(result);
+	}
+}

+ 0 - 4
tests/misc/projects/Issue3238/NonExtern.hx

@@ -1,4 +0,0 @@
-class NonExtern {
-    static function f(a:haxe.extern.Rest<String>):Void {}
-	static function main() {}
-}

+ 0 - 1
tests/misc/projects/Issue3238/non-extern-fail.hxml

@@ -1 +0,0 @@
---run NonExtern

+ 0 - 1
tests/misc/projects/Issue3238/non-extern-fail.hxml.stderr

@@ -1 +0,0 @@
-NonExtern.hx:2: characters 23-24 : Rest argument are only supported for extern methods

+ 1 - 0
tests/unit/src/unit/TestMain.hx

@@ -74,6 +74,7 @@ function main() {
 		new TestNull(),
 		new TestNull(),
 		new TestNumericCasts(),
 		new TestNumericCasts(),
 		new TestHashMap(),
 		new TestHashMap(),
+		new TestRest(),
 		#if (!no_http && (!github || !(php && Windows)))
 		#if (!no_http && (!github || !(php && Windows)))
 		new TestHttp(),
 		new TestHttp(),
 		#end
 		#end

+ 1 - 0
tests/unit/src/unit/TestMatch.hx

@@ -327,6 +327,7 @@ class TestMatch extends Test {
 			case OpIncrement:
 			case OpIncrement:
 			case OpDecrement:
 			case OpDecrement:
 			case OpNot:
 			case OpNot:
+			case OpSpread:
 		}));
 		}));
 		eq("Unmatched patterns: Node(Node, _)", getErrorMessage(switch(Leaf("foo")) {
 		eq("Unmatched patterns: Node(Node, _)", getErrorMessage(switch(Leaf("foo")) {
 			case Node(Leaf("foo"), _):
 			case Node(Leaf("foo"), _):

+ 156 - 0
tests/unit/src/unit/TestRest.hx

@@ -0,0 +1,156 @@
+package unit;
+
+import haxe.Rest;
+
+class TestRest extends Test {
+	function testArrayAccess() {
+		function rest(a:Int, b:Int, ...r:Int) {
+			return r[2];
+		}
+		eq(123, rest(1, 2, 0, 0, 123, 0));
+	}
+
+	function testLength() {
+		function rest(...r:Int) {
+			return r.length;
+		}
+		eq(4, rest(1, 2, 3, 4));
+	}
+
+	function testToArray() {
+		function rest(...r:Int):Array<Int> {
+			var a = r.toArray();
+			a[0] = 999; //make sure array modification doesn't affect rest arguments object
+			return r.toArray();
+		}
+		aeq([1, 2, 3, 4], rest(1, 2, 3, 4));
+	}
+
+	@:depends(testToArray)
+	function testRestReturn() {
+		function rest(...r:Int):Rest<Int> {
+			return r;
+		}
+		aeq([1, 2, 3, 4], rest(1, 2, 3, 4).toArray());
+	}
+
+	function testIterator() {
+		function rest(...r:Int):Array<Int> {
+			return [for(i in r) i];
+		}
+		aeq([3, 2, 1], rest(3, 2, 1));
+	}
+
+	function testKeyValueIterator() {
+		function rest(...r:Int):{keys:Array<Int>, values:Array<Int>} {
+			var keys = [];
+			var values = [];
+			for(k => v in r) {
+				keys.push(k);
+				values.push(v);
+			}
+			return {keys:keys, values:values}
+		}
+		var r = rest(3, 2, 1, 0);
+		aeq([0, 1, 2, 3], r.keys);
+		aeq([3, 2, 1, 0], r.values);
+	}
+
+	@:depends(testToArray)
+	function testAppend() {
+		function rest(...r:Int) {
+			var appended = r.append(9);
+			return {initial:r.toArray(), appended:appended.toArray()}
+		}
+		var result = rest(1, 2);
+		aeq([1, 2], result.initial);
+		aeq([1, 2, 9], result.appended);
+	}
+
+	@:depends(testToArray)
+	function testSpread() {
+		function rest(...r:Int) {
+			return r.toArray();
+		}
+		function spreadRest(...r:Int) {
+			return rest(...r);
+		}
+		aeq([1, 2, 3], rest(...[1, 2, 3]));
+		aeq([3, 2, 1], spreadRest(3, 2, 1));
+		aeq([1, 2, 3], new Parent(...[1, 2, 3]).ctorArgs.toArray());
+	}
+
+	@:depends(testToArray, testRestReturn)
+	function testInheritance() {
+		var p = new Parent(1, 2, 3);
+		var c = new Child(4, 5, 6);
+		aeq([1, 2, 3], p.ctorArgs.toArray());
+		aeq([4, 5, 6, 999], c.ctorArgs.toArray());
+
+		aeq([7, 8, 9], p.methodWithRest(7, 8, 9).toArray());
+		aeq([7, 8, 9, 999], c.methodWithRest(7, 8, 9).toArray());
+	}
+
+	function testInline() {
+		inline function rest(args:Rest<Int>) {
+			return args[2];
+		}
+		eq(3, rest(1, 2, 3, 4));
+		eq(7, rest(...[5, 6, 7, 8]));
+	}
+
+	@:depends(testToArray)
+	function testClosure() {
+		var fn:(...r:Int)->Array<Int>;
+		fn = staticRest;
+		aeq([1, 2, 3], fn(1, 2, 3));
+		aeq([1, 2, 3], fn(...[1, 2, 3]));
+		fn = instanceRest;
+		aeq([3, 2, 1], fn(3, 2, 1));
+		aeq([3, 2, 1], fn(...[3, 2, 1]));
+	}
+	function instanceRest(...r:Int):Array<Int> {
+		return r.toArray();
+	}
+	static function staticRest(...r:Int):Array<Int> {
+		return r.toArray();
+	}
+
+	@:depends(testToArray)
+	function testRestOfArrays() {
+		function rest(...r:Array<Int>) {
+			return r.toArray();
+		}
+		aeq([[1, 2, 3], [6, 5, 4]], rest([1, 2, 3], [6, 5, 4]));
+	}
+
+	@:depends(testToArray)
+	function testRestOfObjects() {
+		function rest(...r:{f:Int}) {
+			return r.toArray();
+		}
+		aeq([{f:2}, {f:1}], rest({f:2}, {f:1}));
+	}
+}
+
+private class Parent {
+	public final ctorArgs:Rest<Int>;
+
+	public function new(rest:Rest<Int>) {
+		ctorArgs = rest;
+	}
+
+	public function methodWithRest(rest:Rest<Int>):Rest<Int> {
+		return rest;
+	}
+}
+
+private class Child extends Parent {
+	public function new(rest:Rest<Int>) {
+		super(...rest.append(999));
+	}
+
+	override public function methodWithRest(rest:Rest<Int>):Rest<Int> {
+		return super.methodWithRest(...rest.append(999));
+	}
+}