Bug 848062 - Make arrow functions inherit the value of 'this' from the enclosing scope. r=bhackett.

This commit is contained in:
Jason Orendorff 2013-03-18 15:32:29 -07:00
parent 7471460fe5
commit 17cbc269fd
9 changed files with 83 additions and 5 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -0,0 +1,5 @@
// 'this' in a toplevel arrow is the global object.
var f = () => this;
assertEq(f(), this);
assertEq({f: f}.f(), this);

View File

@ -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) {

View File

@ -724,6 +724,9 @@ UseNewTypeForClone(JSFunction *fun)
if (fun->nonLazyScript()->shouldCloneAtCallsite)
return true;
if (fun->isArrow())
return true;
if (fun->hasSingletonType())
return false;

View File

@ -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;
}

View File

@ -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