diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 41db6cd609f..8aec70f9a80 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -6929,6 +6929,9 @@ bool IonBuilder::jsop_lambda(JSFunction *fun) { JS_ASSERT(script()->analysis()->usesScopeChain()); + if (fun->isArrow()) + return abort("bound arrow function"); + MLambda *ins = MLambda::New(current->scopeChain(), fun); current->add(ins); current->push(ins); diff --git a/js/src/jit-test/tests/arrow-functions/this-1.js b/js/src/jit-test/tests/arrow-functions/this-1.js new file mode 100644 index 00000000000..1367052700c --- /dev/null +++ b/js/src/jit-test/tests/arrow-functions/this-1.js @@ -0,0 +1,17 @@ +// 'this' is lexically scoped in arrow functions + +var obj = { + f: function (expected) { + assertEq(this, expected); + return a => this; + } +}; + +var g = obj.f(obj); +assertEq(g(), obj); + +var obj2 = {f: obj.f}; +var g2 = obj2.f(obj2); +assertEq(g2(), obj2); +assertEq(g(), obj); + diff --git a/js/src/jit-test/tests/arrow-functions/this-2.js b/js/src/jit-test/tests/arrow-functions/this-2.js new file mode 100644 index 00000000000..72c51558ed6 --- /dev/null +++ b/js/src/jit-test/tests/arrow-functions/this-2.js @@ -0,0 +1,14 @@ +// 'this' is lexically scoped in direct eval code in arrow functions + +var obj = { + f: function (s) { + dis(); + return a => eval(s); + } +}; + +var g = obj.f("this"); +assertEq(g(), obj); + +var obj2 = {g: g, fail: true}; +assertEq(obj2.g(), obj); diff --git a/js/src/jit-test/tests/arrow-functions/this-3.js b/js/src/jit-test/tests/arrow-functions/this-3.js new file mode 100644 index 00000000000..59398a3cb36 --- /dev/null +++ b/js/src/jit-test/tests/arrow-functions/this-3.js @@ -0,0 +1,13 @@ +// 'this' is lexically scoped in arrow functions in direct eval code + +var obj = { + f: function (s) { + return eval(s); + } +}; + +var g = obj.f("a => this"); +assertEq(g(), obj); + +var obj2 = {g: g, fail: true}; +assertEq(obj2.g(), obj); diff --git a/js/src/jit-test/tests/arrow-functions/this-4.js b/js/src/jit-test/tests/arrow-functions/this-4.js new file mode 100644 index 00000000000..0379ef86e5b --- /dev/null +++ b/js/src/jit-test/tests/arrow-functions/this-4.js @@ -0,0 +1,5 @@ +// 'this' in a toplevel arrow is the global object. + +var f = () => this; +assertEq(f(), this); +assertEq({f: f}.f(), this); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 03aa11e8075..665ce651607 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -623,6 +623,16 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb StringBuffer out(cx); RootedScript script(cx); + // If the object is an automatically-bound arrow function, get the source + // of the pre-binding target. + if (fun->isBoundFunction()) { + JSObject *target = fun->getBoundFunctionTarget(); + if (target->isFunction() && target->toFunction()->isArrow()) { + RootedFunction targetfun(cx, target->toFunction()); + return FunctionToString(cx, targetfun, bodyOnly, lambdaParen); + } + } + if (fun->hasScript()) { script = fun->nonLazyScript(); if (script->isGeneratorExp) { diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index e34329a6258..8f205bdc161 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -724,6 +724,9 @@ UseNewTypeForClone(JSFunction *fun) if (fun->nonLazyScript()->shouldCloneAtCallsite) return true; + if (fun->isArrow()) + return true; + if (fun->hasSingletonType()) return false; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index cc13a374dca..7ec946e02f9 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2775,10 +2775,9 @@ BEGIN_CASE(JSOP_LAMBDA) RootedFunction &fun = rootFunction0; fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); - JSFunction *obj = CloneFunctionObjectIfNotSingleton(cx, fun, regs.fp()->scopeChain()); + JSObject *obj = Lambda(cx, fun, regs.fp()->scopeChain()); if (!obj) goto error; - JS_ASSERT(obj->getProto()); PUSH_OBJECT(*obj); } @@ -3461,6 +3460,20 @@ js::Lambda(JSContext *cx, HandleFunction fun, HandleObject parent) if (!clone) return NULL; + if (fun->isArrow()) { + StackFrame *fp = cx->fp(); + + // Note that this will assert if called from Ion code. Ion can't yet + // emit code for a bound arrow function (bug 851913). + if (!ComputeThis(cx, fp)) + return NULL; + + RootedValue thisval(cx, fp->thisValue()); + clone = js_fun_bind(cx, clone, thisval, NULL, 0); + if (!clone) + return NULL; + } + JS_ASSERT(clone->global() == clone->global()); return clone; } diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 7945fea0a87..5f6ff2b294d 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1016,11 +1016,11 @@ JSObject * JS_FASTCALL stubs::Lambda(VMFrame &f, JSFunction *fun_) { RootedFunction fun(f.cx, fun_); - fun = CloneFunctionObjectIfNotSingleton(f.cx, fun, f.fp()->scopeChain()); - if (!fun) + JSObject *clone = Lambda(f.cx, fun, f.fp()->scopeChain()); + if (!clone) THROWV(NULL); - return fun; + return clone; } void JS_FASTCALL