diff --git a/src-json/meta.json b/src-json/meta.json index 5c236f1d950..193fc34fdc9 100644 --- a/src-json/meta.json +++ b/src-json/meta.json @@ -543,6 +543,13 @@ "targets": ["TClass"], "links": ["https://haxe.org/manual/target-javascript-require.html"] }, + { + "name": "JsFunction", + "metadata": ":js.function", + "doc": "Generate `function` keyword instead of arrow function in `-D js_es >= 6` mode to enable access to function scope `this`.", + "platforms": ["js"], + "targets": ["TExpr"] + }, { "name": "LuaRequire", "metadata": ":luaRequire", diff --git a/src/context/common.ml b/src/context/common.ml index 3eef8c80c8b..9a9c74fd6c3 100644 --- a/src/context/common.ml +++ b/src/context/common.ml @@ -482,6 +482,7 @@ let default_config = vs_scope = BlockScope; vs_flags = []; }; + pf_can_capture_this = true; pf_supports_atomics = false; } @@ -516,6 +517,7 @@ let get_config com = (if defined Define.JsUnflatten then ReserveAllTopLevelSymbols else ReserveAllTypesFlat) :: if es6 then [NoShadowing; SwitchCasesNoBlocks;] else [VarHoisting; NoCatchVarShadowing]; }; + pf_can_capture_this = es6; pf_supports_atomics = true; } | Lua -> @@ -527,7 +529,8 @@ let get_config com = pf_supports_rest_args = true; pf_exceptions = { default_config.pf_exceptions with ec_avoid_wrapping = false; - } + }; + pf_can_capture_this = false; } | Neko -> { @@ -542,7 +545,8 @@ let get_config com = }; pf_exceptions = { default_config.pf_exceptions with ec_avoid_wrapping = false - } + }; + pf_can_capture_this = false; } | Flash -> { @@ -567,6 +571,7 @@ let get_config com = vs_scope = FunctionScope; vs_flags = [VarHoisting]; }; + pf_can_capture_this = false; } | Php -> { @@ -603,6 +608,7 @@ let get_config com = vs_flags = [NoShadowing]; vs_scope = FunctionScope; }; + pf_can_capture_this = false; pf_supports_atomics = true; } | Jvm -> @@ -626,6 +632,7 @@ let get_config com = ec_wildcard_catch = (["java";"lang"],"Throwable"); ec_base_throw = (["java";"lang"],"RuntimeException"); }; + pf_can_capture_this = false; pf_supports_atomics = true; } | Python -> @@ -650,6 +657,7 @@ let get_config com = vs_scope = FunctionScope; vs_flags = [VarHoisting] }; + pf_can_capture_this = false; } | Hl -> { @@ -664,7 +672,8 @@ let get_config com = }; pf_exceptions = { default_config.pf_exceptions with ec_avoid_wrapping = false - } + }; + pf_can_capture_this = false; } | Eval -> { @@ -676,7 +685,8 @@ let get_config com = pf_capture_policy = CPWrapRef; pf_exceptions = { default_config.pf_exceptions with ec_avoid_wrapping = false - } + }; + pf_can_capture_this = false; } let memory_marker = [|Unix.time()|] diff --git a/src/context/platformConfig.ml b/src/context/platformConfig.ml index 34474218254..8e7f1a4bb54 100644 --- a/src/context/platformConfig.ml +++ b/src/context/platformConfig.ml @@ -118,6 +118,8 @@ type platform_config = { pf_exceptions : exceptions_config; (** the scoping of local variables *) pf_scoping : var_scoping_config; + (** whether or not the target needs a variable to capture `this` *) + pf_can_capture_this : bool; (** target supports atomic operations via haxe.Atomic **) pf_supports_atomics : bool; -} \ No newline at end of file +} diff --git a/src/generators/genjs.ml b/src/generators/genjs.ml index 3ba99abeae3..c04d79fb5e0 100644 --- a/src/generators/genjs.ml +++ b/src/generators/genjs.ml @@ -569,6 +569,8 @@ and gen_expr ctx e = | TBreak -> print ctx "break _hx_loop%s" n; | _ -> die "" __LOC__) + | TMeta ((Meta.JsFunction, [], _),{eexpr = TFunction f}) -> + gen_function ~hasJsFunction:true ctx f e.epos | TMeta (_,e) -> gen_expr ctx e | TReturn eo -> @@ -733,7 +735,7 @@ and gen_expr ctx e = ); clear_mapping () -and gen_function ?(keyword="function") ctx f pos = +and gen_function ?(keyword="function") ?(hasJsFunction=false) ctx f pos = let old = ctx.in_value, ctx.in_loop in ctx.in_value <- None; ctx.in_loop <- false; @@ -771,7 +773,15 @@ and gen_function ?(keyword="function") ctx f pos = | _ -> f, mk_non_rest_arg_names f.tf_args in - print ctx "%s(%s) " keyword (String.concat "," args); + let can_be_arrow = + keyword = "function" && + ctx.es_version >= 6 && + not hasJsFunction + in + if can_be_arrow then + print ctx "(%s) => " (String.concat "," args) + else + print ctx "%s(%s) " keyword (String.concat "," args); gen_expr ctx (fun_block ctx f pos); ctx.in_value <- fst old; ctx.in_loop <- snd old; @@ -842,6 +852,7 @@ and gen_value ctx e = | TNew _ | TUnop _ | TFunction _ + | TMeta ((Meta.JsFunction, [], _),_) | TIdent _ -> gen_expr ctx e | TMeta (_,e1) -> diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index 784eb25b204..cd4e9da69be 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -465,6 +465,7 @@ and encode_platform_config pc = "supportsRestArgs", vbool pc.pf_supports_rest_args; "exceptions", encode_exceptions_config pc.pf_exceptions; "scoping", encode_var_scoping_config pc.pf_scoping; + "canCaptureThis", vbool pc.pf_can_capture_this; "supportsAtomics", vbool pc.pf_supports_atomics; ] @@ -1723,6 +1724,7 @@ let decode_platform_config v = pf_supports_rest_args = decode_bool (field v "supportsRestArgs"); pf_exceptions = exception_config; pf_scoping = var_scoping_config; + pf_can_capture_this = decode_bool (field v "canCaptureThis"); pf_supports_atomics = decode_bool (field v "supportsAtomics"); } diff --git a/src/optimization/inline.ml b/src/optimization/inline.ml index 75099b27195..35cb080dca4 100644 --- a/src/optimization/inline.ml +++ b/src/optimization/inline.ml @@ -482,7 +482,7 @@ object(self) _inlined_vars <- vars; (* Order is reversed due to tail-recursion *) in match ethis.eexpr with - | TConst TNull -> + | TConst TNull | TConst TThis -> set_inlined_vars params f.tf_args; None | _ -> @@ -712,7 +712,8 @@ let rec type_inline (ictx : inline_context) cf f ethis params tret config p ?(se l.i_read <- l.i_read + (if !in_loop then 2 else 1); { e with eexpr = TLocal l.i_subst } | None -> - raise_typing_error "Could not inline `this` outside of an instance context" po + { e with eexpr = TConst TThis } + (* raise_typing_error "Could not inline `this` outside of an instance context" po *) ) | TVar (v,eo) -> if has_var_flag v VStatic then raise_typing_error "Inline functions cannot have static locals" v.v_pos; diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 834e0250ea8..e8e96e7a914 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -1523,6 +1523,14 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p = | (Meta.NullSafety, [(EConst (Ident "Off"), _)],_) -> let e = e() in {e with eexpr = TMeta(m,e)} + | (Meta.JsFunction, [],pos) when ctx.com.platform=Js -> + let e = e() in + begin match e.eexpr with + | TFunction f -> + {e with eexpr = TMeta(m,e)} + | _ -> + raise_typing_error "@:js.function can be applied only to anonymous functions" pos + end | (Meta.BypassAccessor,_,p) -> let old_counter = ctx.e.bypass_accessor in ctx.e.bypass_accessor <- old_counter + 1; diff --git a/src/typing/typerBase.ml b/src/typing/typerBase.ml index e6cb480d21d..47d09b66b7f 100644 --- a/src/typing/typerBase.ml +++ b/src/typing/typerBase.ml @@ -149,10 +149,17 @@ let is_lower_ident s p = with Invalid_argument msg -> raise_typing_error msg p let get_this ctx p = + let has_jsfun_meta metas = + ctx.com.platform == Js && List.exists (fun (name,args,pos) -> + match name with + | Meta.JsFunction -> true + | _ -> false + ) metas + in match ctx.e.curfun with | FunStatic -> raise_typing_error "Cannot access this from a static function" p - | FunMemberClassLocal | FunMemberAbstractLocal -> + | FunMemberClassLocal | FunMemberAbstractLocal when not ctx.com.config.pf_can_capture_this || has_jsfun_meta ctx.f.meta -> let v = match ctx.f.vthis with | None -> let v = if ctx.e.curfun = FunMemberAbstractLocal then begin @@ -169,10 +176,10 @@ let get_this ctx p = v in mk (TLocal v) ctx.c.tthis p - | FunMemberAbstract -> + | FunMemberAbstract | FunMemberAbstractLocal -> let v = (try PMap.find "this" ctx.f.locals with Not_found -> raise_typing_error "Cannot reference this abstract here" p) in mk (TLocal v) v.v_type p - | FunConstructor | FunMember -> + | FunConstructor | FunMember | FunMemberClassLocal -> mk (TConst TThis) ctx.c.tthis p let get_stored_typed_expr ctx id = @@ -406,4 +413,4 @@ let get_safe_nav_base ctx eobj = let v = alloc_var VGenerated "tmp" eobj.etype eobj.epos in let temp_var = mk (TVar(v, Some eobj)) ctx.t.tvoid v.v_pos in let eobj = mk (TLocal v) v.v_type v.v_pos in - eobj, Some temp_var \ No newline at end of file + eobj, Some temp_var diff --git a/std/haxe/macro/PlatformConfig.hx b/std/haxe/macro/PlatformConfig.hx index 787e88016be..03d154ee896 100644 --- a/std/haxe/macro/PlatformConfig.hx +++ b/std/haxe/macro/PlatformConfig.hx @@ -91,6 +91,10 @@ typedef PlatformConfig = { **/ final supportsAtomics:Bool; + /** + Whether or not the target needs a variable to capture `this` + **/ + final canCaptureThis:Bool; } enum CapturePolicy { diff --git a/std/js/_std/HxOverrides.hx b/std/js/_std/HxOverrides.hx index 8f55624dec0..655450fee7e 100644 --- a/std/js/_std/HxOverrides.hx +++ b/std/js/_std/HxOverrides.hx @@ -129,10 +129,10 @@ class HxOverrides { return { cur: 0, arr: a, - hasNext: function() { + hasNext: @:js.function function() { return __this__.cur < __this__.arr.length; }, - next: function() { + next: @:js.function function() { return __this__.arr[__this__.cur++]; } }; diff --git a/std/js/_std/Reflect.hx b/std/js/_std/Reflect.hx index db99e909748..6bc66e976da 100644 --- a/std/js/_std/Reflect.hx +++ b/std/js/_std/Reflect.hx @@ -110,7 +110,7 @@ } public static function makeVarArgs(f:Array->T):Dynamic { - return function() { + return @:js.function function() { var a = untyped Array.prototype.slice.call(js.Syntax.code("arguments")); return f(a); }; diff --git a/std/js/_std/Std.hx b/std/js/_std/Std.hx index 55cdacd1c44..9f6873d3373 100644 --- a/std/js/_std/Std.hx +++ b/std/js/_std/Std.hx @@ -102,13 +102,13 @@ import js.Syntax; __feature__("Class.*", js.Syntax.code('var Class = { };')); __feature__("Enum.*", js.Syntax.code('var Enum = { };')); #if (js_es < 5) - __feature__("Array.map", if (Array.prototype.map == null) Array.prototype.map = function(f) { + __feature__("Array.map", if (Array.prototype.map == null) Array.prototype.map = @:js.function function(f) { var a = []; for (i in 0...__this__.length) a[i] = f(__this__[i]); return a; }); - __feature__("Array.filter", if (Array.prototype.filter == null) Array.prototype.filter = function(f) { + __feature__("Array.filter", if (Array.prototype.filter == null) Array.prototype.filter = @:js.function function(f) { var a = []; for (i in 0...__this__.length) { var e = __this__[i]; diff --git a/std/js/_std/haxe/ds/IntMap.hx b/std/js/_std/haxe/ds/IntMap.hx index 4804006cc53..c41d0009d2e 100644 --- a/std/js/_std/haxe/ds/IntMap.hx +++ b/std/js/_std/haxe/ds/IntMap.hx @@ -120,10 +120,10 @@ package haxe.ds; return untyped { ref: h, it: keys(), - hasNext: function() { + hasNext: @:js.function function() { return __this__.it.hasNext(); }, - next: function() { + next: @:js.function function() { var i = __this__.it.next(); return __this__.ref[i]; } diff --git a/std/js/_std/haxe/ds/ObjectMap.hx b/std/js/_std/haxe/ds/ObjectMap.hx index 2c25bc7ad8c..ed01e32478e 100644 --- a/std/js/_std/haxe/ds/ObjectMap.hx +++ b/std/js/_std/haxe/ds/ObjectMap.hx @@ -145,10 +145,10 @@ class ObjectMap implements haxe.Constraints.IMap { return untyped { ref: h, it: keys(), - hasNext: function() { + hasNext: @:js.function function() { return __this__.it.hasNext(); }, - next: function() { + next: @:js.function function() { var i = __this__.it.next(); return __this__.ref[getId(i)]; } diff --git a/tests/misc/projects/Issue11128/hxtest/Init.hx b/tests/misc/projects/Issue11128/hxtest/Init.hx index 90259789ccd..93b3478b4b3 100644 --- a/tests/misc/projects/Issue11128/hxtest/Init.hx +++ b/tests/misc/projects/Issue11128/hxtest/Init.hx @@ -7,6 +7,7 @@ class Init { // Default, except everything has been set to `true`. public static var intendedConfig: PlatformConfig = { supportsAtomics: true, + canCaptureThis: true, thisBeforeSuper: true, scoping: { scope: BlockScope,