Bug 561359 - Change JSOP_LAMBDA to apply the method optimization deterministically. In particular, it no longer depends on whether enclosing Blocks have been reified. This prevents incorrect behavior and assertions when a JSOP_LAMBDA, JSOP_INITMETHOD pair apply the method optimization once, populating the property cache, but later the same JSOP_LAMBDA instruction does not (under the old code) apply the optimization. With this patch, if JSOP_LAMBDA pushes the uncloned function once, it always will. r=brendan.

This commit is contained in:
Jason Orendorff 2011-03-14 15:54:34 -05:00
parent f93c39ea90
commit efd9982808
10 changed files with 171 additions and 112 deletions

View File

@ -218,8 +218,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
/* /*
* Wrappers should really be parented to the wrapped parent of the wrapped * Wrappers should really be parented to the wrapped parent of the wrapped
* object, but in that case a wrapped global object would have a NULL * object, but in that case a wrapped global object would have a NULL
* parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
,
* we parent all wrappers to the global object in their home compartment. * we parent all wrappers to the global object in their home compartment.
* This loses us some transparency, and is generally very cheesy. * This loses us some transparency, and is generally very cheesy.
*/ */

View File

@ -5588,10 +5588,9 @@ BEGIN_CASE(JSOP_LAMBDA)
/* do-while(0) so we can break instead of using a goto. */ /* do-while(0) so we can break instead of using a goto. */
do { do {
JSObject *parent; JSObject *parent;
if (FUN_NULL_CLOSURE(fun)) { if (fun->joinable()) {
parent = &regs.fp->scopeChain(); parent = &regs.fp->scopeChain();
if (obj->getParent() == parent) {
jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH); jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH);
JSOp op2 = JSOp(*pc2); JSOp op2 = JSOp(*pc2);
@ -5670,7 +5669,6 @@ BEGIN_CASE(JSOP_LAMBDA)
} }
} }
} }
}
#ifdef DEBUG #ifdef DEBUG
if (rt->functionMeterFilename) { if (rt->functionMeterFilename) {

View File

@ -313,7 +313,9 @@ bool
JSFunctionBox::joinable() const JSFunctionBox::joinable() const
{ {
return FUN_NULL_CLOSURE((JSFunction *) object) && return FUN_NULL_CLOSURE((JSFunction *) object) &&
!(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)); (tcflags & (TCF_FUN_USES_ARGUMENTS |
TCF_FUN_USES_OWN_NAME |
TCF_COMPILE_N_GO)) == TCF_COMPILE_N_GO;
} }
bool bool
@ -4732,8 +4734,6 @@ CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
#endif /* JS_HAS_DESTRUCTURING */ #endif /* JS_HAS_DESTRUCTURING */
extern const char js_with_statement_str[];
static JSParseNode * static JSParseNode *
ContainsStmt(JSParseNode *pn, TokenKind tt) ContainsStmt(JSParseNode *pn, TokenKind tt)
{ {

View File

@ -15556,7 +15556,7 @@ TraceRecorder::record_JSOP_LAMBDA()
* JSOP_INITMETHOD logic governing the early ARECORD_CONTINUE returns below * JSOP_INITMETHOD logic governing the early ARECORD_CONTINUE returns below
* must agree with the corresponding break-from-do-while(0) logic there. * must agree with the corresponding break-from-do-while(0) logic there.
*/ */
if (FUN_NULL_CLOSURE(fun) && FUN_OBJECT(fun)->getParent() == &cx->fp()->scopeChain()) { if (fun->joinable()) {
jsbytecode *pc2 = AdvanceOverBlockchainOp(cx->regs->pc + JSOP_LAMBDA_LENGTH); jsbytecode *pc2 = AdvanceOverBlockchainOp(cx->regs->pc + JSOP_LAMBDA_LENGTH);
JSOp op2 = JSOp(*pc2); JSOp op2 = JSOp(*pc2);

View File

@ -1419,33 +1419,42 @@ stubs::RegExp(VMFrame &f, JSObject *regex)
JSObject * JS_FASTCALL JSObject * JS_FASTCALL
stubs::LambdaForInit(VMFrame &f, JSFunction *fun) stubs::LambdaForInit(VMFrame &f, JSFunction *fun)
{ {
JS_ASSERT(fun->joinable());
JS_ASSERT(FUN_NULL_CLOSURE(fun));
JS_ASSERT(f.fp()->script()->compileAndGo);
JSObject *obj = FUN_OBJECT(fun); JSObject *obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { JS_ASSERT(obj->getParent() != NULL);
fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc))); fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
return obj; return obj;
}
return Lambda(f, fun);
} }
JSObject * JS_FASTCALL JSObject * JS_FASTCALL
stubs::LambdaForSet(VMFrame &f, JSFunction *fun) stubs::LambdaForSet(VMFrame &f, JSFunction *fun)
{ {
JS_ASSERT(fun->joinable());
JS_ASSERT(FUN_NULL_CLOSURE(fun));
JS_ASSERT(f.fp()->script()->compileAndGo);
JSObject *obj = FUN_OBJECT(fun); JSObject *obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { JS_ASSERT(obj->getParent() != NULL);
const Value &lref = f.regs.sp[-1]; const Value &lref = f.regs.sp[-1];
if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc))); fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(f.regs.pc)));
return obj; return obj;
} }
}
return Lambda(f, fun); return Lambda(f, fun);
} }
JSObject * JS_FASTCALL JSObject * JS_FASTCALL
stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun) stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
{ {
JS_ASSERT(fun->joinable());
JS_ASSERT(FUN_NULL_CLOSURE(fun));
JS_ASSERT(f.fp()->script()->compileAndGo);
JSObject *obj = FUN_OBJECT(fun); JSObject *obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun) && obj->getParent() == &f.fp()->scopeChain()) { JS_ASSERT(obj->getParent() != NULL);
/* /*
* Array.prototype.sort and String.prototype.replace are * Array.prototype.sort and String.prototype.replace are
* optimized as if they are special form. We know that they * optimized as if they are special form. We know that they
@ -1474,7 +1483,6 @@ stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
return obj; return obj;
} }
} }
}
return Lambda(f, fun); return Lambda(f, fun);
} }

View File

@ -21,6 +21,10 @@ script regress-559438.js
script regress-560101.js script regress-560101.js
script regress-560998-1.js script regress-560998-1.js
script regress-560998-2.js script regress-560998-2.js
script regress-561359-1.js
script regress-561359-2.js
script regress-561359-3.js
script regress-561359-4.js
script regress-563210.js script regress-563210.js
script regress-563221.js script regress-563221.js
script regress-566549.js script regress-566549.js

View File

@ -0,0 +1,13 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Contributor: Gary Kwong <gary@rumblingedge.com>
for (let z = 0; z < 2; z++) {
with({
x: function() {}
}) {
for (l in [x]) {}
}
}
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,14 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Contributor: Jason Orendorff <jorendorff@mozilla.com>
function f(s) {
var obj = {m: function () { return a; }};
eval(s);
return obj;
}
var obj = f("var a = 'right';");
var a = 'wrong';
assertEq(obj.m(), 'right');
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,13 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Contributor: Jason Orendorff <jorendorff@mozilla.com>
function f(s) {
with (s)
return {m: function () { return a; }};
}
var obj = f({a: 'right'});
var a = 'wrong';
assertEq(obj.m(), 'right');
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,10 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
// Contributor: Jason Orendorff <jorendorff@mozilla.com>
var x;
for (let i = 0; i < 2; i++)
x = {m: function () {}, n: function () { i++; }};
x.m;
reportCompare(0, 0, 'ok');