зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 876c8ee6b132 (bug 1141862) for spidermonkey bustage
This commit is contained in:
Родитель
2a9cba85f6
Коммит
89ea761a32
|
@ -281,12 +281,10 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
|
|||
if (!parser.checkOptions())
|
||||
return nullptr;
|
||||
|
||||
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
|
||||
bool allowSuperProperty = savedCallerFun && evalCaller->functionOrCallerFunction()->isMethod();
|
||||
|
||||
Directives directives(options.strictOption);
|
||||
GlobalSharedContext globalsc(cx, directives, options.extraWarningsOption, allowSuperProperty);
|
||||
GlobalSharedContext globalsc(cx, directives, options.extraWarningsOption);
|
||||
|
||||
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
|
||||
options, staticLevel, sourceObject, 0,
|
||||
srcBuf.length()));
|
||||
|
|
|
@ -1991,7 +1991,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
case PNK_DOT:
|
||||
case PNK_CALL:
|
||||
case PNK_ELEM:
|
||||
case PNK_SUPERELEM:
|
||||
/* All these delete addressing modes have effects too. */
|
||||
*answer = true;
|
||||
return true;
|
||||
|
@ -2054,8 +2053,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
return checkSideEffects(pn->maybeExpr(), answer);
|
||||
|
||||
case PN_NULLARY:
|
||||
if (pn->isKind(PNK_DEBUGGER) ||
|
||||
pn->isKind(PNK_SUPERPROP))
|
||||
if (pn->isKind(PNK_DEBUGGER))
|
||||
*answer = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -2324,18 +2322,6 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn)
|
|||
return emitTree(pn2);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperPropLHS(bool isCall)
|
||||
{
|
||||
if (!emit1(JSOP_THIS))
|
||||
return false;
|
||||
if (isCall && !emit1(JSOP_DUP))
|
||||
return false;
|
||||
if (!emit1(JSOP_SUPERBASE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
||||
{
|
||||
|
@ -2356,25 +2342,10 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall)
|
||||
{
|
||||
if (!emitSuperPropLHS(isCall))
|
||||
return false;
|
||||
|
||||
if (!emitAtomOp(pn, op))
|
||||
return false;
|
||||
|
||||
if (isCall && !emit1(JSOP_SWAP))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitPropIncDec(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_kid->isKind(PNK_DOT));
|
||||
MOZ_ASSERT(pn->pn_kid->getKind() == PNK_DOT);
|
||||
|
||||
bool post;
|
||||
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
|
||||
|
@ -2410,50 +2381,6 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperPropIncDec(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_kid->isKind(PNK_SUPERPROP));
|
||||
|
||||
bool post;
|
||||
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
|
||||
|
||||
if (!emitSuperPropLHS()) // THIS OBJ
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_DUP2)) // THIS OBJ THIS OBJ
|
||||
return false;
|
||||
if (!emitAtomOp(pn->pn_kid, JSOP_GETPROP_SUPER)) // THIS OBJ V
|
||||
return false;
|
||||
if (!emit1(JSOP_POS)) // THIS OBJ N
|
||||
return false;
|
||||
if (post && !emit1(JSOP_DUP)) // THIS OBJ N? N
|
||||
return false;
|
||||
if (!emit1(JSOP_ONE)) // THIS OBJ N? N 1
|
||||
return false;
|
||||
if (!emit1(binop)) // THIS OBJ N? N+1
|
||||
return false;
|
||||
|
||||
if (post) {
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)3)) // OBJ N N+1 THIS
|
||||
return false;
|
||||
if (!emit1(JSOP_SWAP)) // OBJ N THIS N+1
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)3)) // N THIS N+1 OBJ
|
||||
return false;
|
||||
if (!emit1(JSOP_SWAP)) // N THIS OBJ N+1
|
||||
return false;
|
||||
}
|
||||
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
|
||||
if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1
|
||||
return false;
|
||||
if (post && !emit1(JSOP_POP)) // RESULT
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitNameIncDec(ParseNode* pn)
|
||||
{
|
||||
|
@ -2496,61 +2423,18 @@ bool
|
|||
BytecodeEmitter::emitElemOperands(ParseNode* pn, JSOp op)
|
||||
{
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
||||
if (!emitTree(pn->pn_left))
|
||||
return false;
|
||||
|
||||
if (op == JSOP_CALLELEM && !emit1(JSOP_DUP))
|
||||
return false;
|
||||
|
||||
if (!emitTree(pn->pn_right))
|
||||
return false;
|
||||
|
||||
bool isSetElem = op == JSOP_SETELEM || op == JSOP_STRICTSETELEM;
|
||||
if (isSetElem && !emit2(JSOP_PICK, (jsbytecode)2))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_SUPERELEM));
|
||||
|
||||
// The ordering here is somewhat screwy. We need to evaluate the propval
|
||||
// first, by spec. Do a little dance to not emit more than one JSOP_THIS.
|
||||
// Since JSOP_THIS might throw in derived class constructors, we cannot
|
||||
// just push it earlier as the receiver. We have to swap it down instead.
|
||||
|
||||
if (!emitTree(pn->pn_kid))
|
||||
return false;
|
||||
|
||||
// We need to convert the key to an object id first, so that we do not do
|
||||
// it inside both the GETELEM and the SETELEM.
|
||||
if (opts == SuperElem_IncDec && !emit1(JSOP_TOID))
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_THIS))
|
||||
return false;
|
||||
|
||||
if (opts == SuperElem_Call) {
|
||||
if (!emit1(JSOP_SWAP))
|
||||
return false;
|
||||
|
||||
// We need another |this| on top, also
|
||||
if (!emitDupAt(this->stackDepth - 1 - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_SUPERBASE))
|
||||
return false;
|
||||
|
||||
if (opts == SuperElem_Set && !emit2(JSOP_PICK, (jsbytecode)3))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitElemOpBase(JSOp op)
|
||||
{
|
||||
|
@ -2567,30 +2451,10 @@ BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op)
|
|||
return emitElemOperands(pn, op) && emitElemOpBase(op);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall)
|
||||
{
|
||||
SuperElemOptions opts = SuperElem_Get;
|
||||
if (isCall)
|
||||
opts = SuperElem_Call;
|
||||
else if (op == JSOP_SETELEM_SUPER || op == JSOP_STRICTSETELEM_SUPER)
|
||||
opts = SuperElem_Set;
|
||||
|
||||
if (!emitSuperElemOperands(pn, opts))
|
||||
return false;
|
||||
if (!emitElemOpBase(op))
|
||||
return false;
|
||||
|
||||
if (isCall && !emit1(JSOP_SWAP))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitElemIncDec(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM));
|
||||
MOZ_ASSERT(pn->pn_kid->getKind() == PNK_ELEM);
|
||||
|
||||
if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM))
|
||||
return false;
|
||||
|
@ -2636,55 +2500,6 @@ BytecodeEmitter::emitElemIncDec(ParseNode* pn)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitSuperElemIncDec(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_kid->isKind(PNK_SUPERELEM));
|
||||
|
||||
if (!emitSuperElemOperands(pn->pn_kid, SuperElem_IncDec))
|
||||
return false;
|
||||
|
||||
bool post;
|
||||
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
|
||||
|
||||
// There's no such thing as JSOP_DUP3, so we have to be creative.
|
||||
// Note that pushing things again is no fewer JSOps.
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2)) // KEY THIS OBJ KEY
|
||||
return false;
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2)) // KEY THIS OBJ KEY THIS
|
||||
return false;
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2)) // KEY THIS OBJ KEY THIS OBJ
|
||||
return false;
|
||||
if (!emitElemOpBase(JSOP_GETELEM_SUPER)) // KEY THIS OBJ V
|
||||
return false;
|
||||
if (!emit1(JSOP_POS)) // KEY THIS OBJ N
|
||||
return false;
|
||||
if (post && !emit1(JSOP_DUP)) // KEY THIS OBJ N? N
|
||||
return false;
|
||||
if (!emit1(JSOP_ONE)) // KEY THIS OBJ N? N 1
|
||||
return false;
|
||||
if (!emit1(binop)) // KEY THIS OBJ N? N+1
|
||||
return false;
|
||||
|
||||
if (post) {
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)4)) // THIS OBJ N N+1 KEY
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)4)) // OBJ N N+1 KEY THIS
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)4)) // N N+1 KEY THIS OBJ
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, (jsbytecode)3)) // N KEY THIS OBJ N+1
|
||||
return false;
|
||||
}
|
||||
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
|
||||
if (!emitElemOpBase(setOp)) // N? N+1
|
||||
return false;
|
||||
if (post && !emit1(JSOP_POP)) // RESULT
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
BytecodeEmitter::emitNumberOp(double dval)
|
||||
{
|
||||
|
@ -3462,40 +3277,17 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, VarEmitOption emitOptio
|
|||
break;
|
||||
}
|
||||
|
||||
case PNK_SUPERPROP:
|
||||
{
|
||||
// See comment above at PNK_DOT. Pick up the pushed value, to fix ordering.
|
||||
if (!emitSuperPropLHS())
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, 2))
|
||||
return false;
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
|
||||
if (!emitAtomOp(target, setOp))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_ELEM:
|
||||
{
|
||||
// See the comment at `case PNK_DOT:` above. This case,
|
||||
// `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP
|
||||
// is emitted by emitElemOperands.
|
||||
// is emitted by EmitElemOperands.
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
if (!emitElemOp(target, setOp))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_SUPERELEM:
|
||||
{
|
||||
// See comment above in the PNK_ELEM case. Just as there, the
|
||||
// reordering is handled by emitSuperElemOp.
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
|
||||
if (!emitSuperElemOp(target, setOp))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_CALL:
|
||||
MOZ_ASSERT(target->pn_xflags & PNX_SETCALL);
|
||||
if (!emitTree(target))
|
||||
|
@ -4100,13 +3892,6 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
|||
if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
|
||||
return false;
|
||||
break;
|
||||
case PNK_SUPERPROP:
|
||||
if (!emitSuperPropLHS())
|
||||
return false;
|
||||
offset += 2;
|
||||
if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
|
||||
return false;
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
MOZ_ASSERT(lhs->isArity(PN_BINARY));
|
||||
if (!emitTree(lhs->pn_left))
|
||||
|
@ -4115,11 +3900,6 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
|||
return false;
|
||||
offset += 2;
|
||||
break;
|
||||
case PNK_SUPERELEM:
|
||||
if (!emitSuperElemOperands(lhs))
|
||||
return false;
|
||||
offset += 3;
|
||||
break;
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
break;
|
||||
|
@ -4183,28 +3963,12 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_SUPERPROP:
|
||||
if (!emit1(JSOP_DUP2))
|
||||
return false;
|
||||
if (!emitIndex32(JSOP_GETPROP_SUPER, atomIndex))
|
||||
return false;
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
if (!emit1(JSOP_DUP2))
|
||||
return false;
|
||||
if (!emitElemOpBase(JSOP_GETELEM))
|
||||
return false;
|
||||
break;
|
||||
case PNK_SUPERELEM:
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2))
|
||||
return false;
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2))
|
||||
return false;
|
||||
if (!emitDupAt(this->stackDepth - 1 - 2))
|
||||
return false;
|
||||
if (!emitElemOpBase(JSOP_GETELEM_SUPER))
|
||||
return false;
|
||||
break;
|
||||
case PNK_CALL:
|
||||
/*
|
||||
* We just emitted a JSOP_SETCALL (which will always throw) and
|
||||
|
@ -4268,13 +4032,6 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_SUPERPROP:
|
||||
{
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER;
|
||||
if (!emitIndexOp(setOp, atomIndex))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_CALL:
|
||||
/* Do nothing. The JSOP_SETCALL we emitted will always throw. */
|
||||
MOZ_ASSERT(lhs->pn_xflags & PNX_SETCALL);
|
||||
|
@ -4286,13 +4043,6 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs)
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_SUPERELEM:
|
||||
{
|
||||
JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER;
|
||||
if (!emit1(setOp))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
if (!emitDestructuringOps(lhs))
|
||||
|
@ -6149,15 +5899,6 @@ BytecodeEmitter::emitDelete(ParseNode* pn)
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_SUPERPROP:
|
||||
// Still have to calculate the base, even though we are are going
|
||||
// to throw unconditionally, as calculating the base could also
|
||||
// throw.
|
||||
if (!emit1(JSOP_SUPERBASE))
|
||||
return false;
|
||||
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
|
||||
return false;
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
{
|
||||
JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
|
||||
|
@ -6165,21 +5906,6 @@ BytecodeEmitter::emitDelete(ParseNode* pn)
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_SUPERELEM:
|
||||
// Still have to calculate everything, even though we're gonna throw
|
||||
// since it may have side effects
|
||||
if (!emitTree(pn2->pn_kid))
|
||||
return false;
|
||||
if (!emit1(JSOP_SUPERBASE))
|
||||
return false;
|
||||
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
|
||||
return false;
|
||||
|
||||
// Another wrinkle: Balance the stack from the emitter's point of view.
|
||||
// Execution will not reach here, as the last bytecode threw.
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
/*
|
||||
|
@ -6343,10 +6069,6 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
|||
if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
|
||||
return false;
|
||||
break;
|
||||
case PNK_SUPERPROP:
|
||||
if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
|
||||
return false;
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM))
|
||||
return false;
|
||||
|
@ -6355,10 +6077,6 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
case PNK_SUPERELEM:
|
||||
if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop))
|
||||
return false;
|
||||
break;
|
||||
case PNK_FUNCTION:
|
||||
/*
|
||||
* Top level lambdas which are immediately invoked should be
|
||||
|
@ -6502,18 +6220,10 @@ BytecodeEmitter::emitIncOrDec(ParseNode* pn)
|
|||
if (!emitPropIncDec(pn))
|
||||
return false;
|
||||
break;
|
||||
case PNK_SUPERPROP:
|
||||
if (!emitSuperPropIncDec(pn))
|
||||
return false;
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
if (!emitElemIncDec(pn))
|
||||
return false;
|
||||
break;
|
||||
case PNK_SUPERELEM:
|
||||
if (!emitSuperElemIncDec(pn))
|
||||
return false;
|
||||
break;
|
||||
case PNK_CALL:
|
||||
MOZ_ASSERT(pn2->pn_xflags & PNX_SETCALL);
|
||||
if (!emitTree(pn2))
|
||||
|
@ -6740,14 +6450,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
|||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
|
||||
objp.set(nullptr);
|
||||
|
||||
if (propdef->pn_right->isKind(PNK_FUNCTION) &&
|
||||
propdef->pn_right->pn_funbox->needsHomeObject())
|
||||
{
|
||||
MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->isMethod());
|
||||
if (!emit1(JSOP_INITHOMEOBJECT))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isIndex) {
|
||||
objp.set(nullptr);
|
||||
switch (op) {
|
||||
|
@ -7075,38 +6777,27 @@ BytecodeEmitter::emitClass(ParseNode* pn)
|
|||
return false;
|
||||
}
|
||||
|
||||
// This is kind of silly. In order to the get the home object defined on
|
||||
// the constructor, we have to make it second, but we want the prototype
|
||||
// on top for EmitPropertyList, because we expect static properties to be
|
||||
// rarer. The result is a few more swaps than we would like. Such is life.
|
||||
if (heritageExpression) {
|
||||
if (!emitTree(heritageExpression))
|
||||
return false;
|
||||
if (!emit1(JSOP_CLASSHERITAGE))
|
||||
return false;
|
||||
if (!emit1(JSOP_OBJWITHPROTO))
|
||||
return false;
|
||||
|
||||
// JSOP_CLASSHERITAGE leaves both protos on the stack. After
|
||||
// creating the prototype, swap it to the bottom to make the
|
||||
// constructor.
|
||||
if (!emit1(JSOP_SWAP))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitFunction(constructor, !!heritageExpression))
|
||||
return false;
|
||||
|
||||
if (constructor->pn_funbox->needsHomeObject()) {
|
||||
if (!emit1(JSOP_INITHOMEOBJECT))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (heritageExpression) {
|
||||
// JSOP_CLASSHERITAGE leaves both prototypes on the stack. After
|
||||
// creating the constructor, trickly it to the bottom to make the object.
|
||||
if (!emit1(JSOP_SWAP))
|
||||
return false;
|
||||
if (!emit1(JSOP_OBJWITHPROTO))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_DUP2))
|
||||
return false;
|
||||
|
@ -7453,20 +7144,10 @@ BytecodeEmitter::emitTree(ParseNode* pn)
|
|||
ok = emitPropOp(pn, JSOP_GETPROP);
|
||||
break;
|
||||
|
||||
case PNK_SUPERPROP:
|
||||
if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_ELEM:
|
||||
ok = emitElemOp(pn, JSOP_GETELEM);
|
||||
break;
|
||||
|
||||
case PNK_SUPERELEM:
|
||||
if (!emitSuperElemOp(pn, JSOP_GETELEM_SUPER))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_NEW:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
case PNK_CALL:
|
||||
|
|
|
@ -590,13 +590,6 @@ struct BytecodeEmitter
|
|||
bool emitForOf(StmtType type, ParseNode* pn, ptrdiff_t top);
|
||||
|
||||
bool emitClass(ParseNode* pn);
|
||||
bool emitSuperPropLHS(bool isCall = false);
|
||||
bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false);
|
||||
bool emitSuperPropIncDec(ParseNode* pn);
|
||||
enum SuperElemOptions { SuperElem_Get, SuperElem_Set, SuperElem_Call, SuperElem_IncDec };
|
||||
bool emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts = SuperElem_Get);
|
||||
bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
|
||||
bool emitSuperElemIncDec(ParseNode* pn);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
|
|
|
@ -419,8 +419,6 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_CLASSMETHOD:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_SUPERELEM:
|
||||
MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
|
||||
"some parent node without recurring to test this node");
|
||||
|
||||
|
|
|
@ -290,12 +290,6 @@ class FullParseHandler
|
|||
ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) {
|
||||
return new_<ClassNames>(outer, inner, pos);
|
||||
}
|
||||
ParseNode* newSuperProperty(JSAtom* atom, const TokenPos& pos) {
|
||||
return new_<SuperProperty>(atom, pos);
|
||||
}
|
||||
ParseNode* newSuperElement(ParseNode* expr, const TokenPos& pos) {
|
||||
return new_<SuperElement>(expr, pos);
|
||||
}
|
||||
|
||||
bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
|
||||
// Object literals with mutated [[Prototype]] are non-constant so that
|
||||
|
|
|
@ -214,7 +214,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_SUPERPROP:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
|
||||
MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately");
|
||||
|
@ -238,7 +237,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_SPREAD:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_EXPORT:
|
||||
case PNK_SUPERELEM:
|
||||
return PushUnaryNodeChild(pn, stack);
|
||||
|
||||
// Nodes with a single nullable child.
|
||||
|
|
|
@ -154,8 +154,6 @@ class UpvarCookie
|
|||
F(CLASSMETHOD) \
|
||||
F(CLASSMETHODLIST) \
|
||||
F(CLASSNAMES) \
|
||||
F(SUPERPROP) \
|
||||
F(SUPERELEM) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOF) \
|
||||
|
@ -1427,37 +1425,6 @@ struct ClassNode : public TernaryNode {
|
|||
}
|
||||
};
|
||||
|
||||
struct SuperProperty : public NullaryNode {
|
||||
SuperProperty(JSAtom* atom, const TokenPos& pos)
|
||||
: NullaryNode(PNK_SUPERPROP, JSOP_NOP, pos, atom)
|
||||
{ }
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
bool match = node.isKind(PNK_SUPERPROP);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
JSAtom* propName() const {
|
||||
return pn_atom;
|
||||
}
|
||||
};
|
||||
|
||||
struct SuperElement : public UnaryNode {
|
||||
SuperElement(ParseNode* expr, const TokenPos& pos)
|
||||
: UnaryNode(PNK_SUPERELEM, JSOP_NOP, pos, expr)
|
||||
{ }
|
||||
|
||||
static bool test(const ParseNode& node) {
|
||||
bool match = node.isKind(PNK_SUPERELEM);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_UNARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
ParseNode* expr() const {
|
||||
return pn_kid;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
void DumpParseTree(ParseNode* pn, int indent = 0);
|
||||
|
|
|
@ -698,8 +698,7 @@ Parser<ParseHandler>::parse(JSObject* chain)
|
|||
* protected from the GC by a root or a stack frame reference.
|
||||
*/
|
||||
Directives directives(options().strictOption);
|
||||
GlobalSharedContext globalsc(context, directives, options().extraWarningsOption,
|
||||
/* allowSuperProperty = */ false);
|
||||
GlobalSharedContext globalsc(context, directives, options().extraWarningsOption);
|
||||
ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
|
||||
&globalsc, /* newDirectives = */ nullptr,
|
||||
/* staticLevel = */ 0, /* bodyid = */ 0,
|
||||
|
@ -1230,7 +1229,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, Hand
|
|||
}
|
||||
|
||||
gc::AllocKind allocKind = JSFunction::FinalizeKind;
|
||||
if (kind == Arrow || kind == Method)
|
||||
if (kind == Arrow)
|
||||
allocKind = JSFunction::ExtendedFinalizeKind;
|
||||
fun = NewFunctionWithProto(context, nullptr, 0, flags, NullPtr(), atom, proto,
|
||||
allocKind, TenuredObject);
|
||||
|
@ -4576,9 +4575,7 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode* pn1, JSVersion versi
|
|||
case PNK_ARRAY:
|
||||
case PNK_CALL:
|
||||
case PNK_DOT:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_ELEM:
|
||||
case PNK_SUPERELEM:
|
||||
case PNK_NAME:
|
||||
case PNK_OBJECT:
|
||||
return true;
|
||||
|
@ -6324,8 +6321,6 @@ Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentF
|
|||
|
||||
case PNK_DOT:
|
||||
case PNK_ELEM:
|
||||
case PNK_SUPERPROP:
|
||||
case PNK_SUPERELEM:
|
||||
break;
|
||||
|
||||
case PNK_ARRAY:
|
||||
|
@ -6494,8 +6489,6 @@ Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode* kid, TokenKind tt,
|
|||
// Check.
|
||||
if (!kid->isKind(PNK_NAME) &&
|
||||
!kid->isKind(PNK_DOT) &&
|
||||
!kid->isKind(PNK_SUPERPROP) &&
|
||||
!kid->isKind(PNK_SUPERELEM) &&
|
||||
!kid->isKind(PNK_ELEM) &&
|
||||
!(kid->isKind(PNK_CALL) &&
|
||||
(kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
|
||||
|
@ -7681,24 +7674,6 @@ Parser<ParseHandler>::argumentList(Node listNode, bool* isSpread)
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkAndMarkSuperScope()
|
||||
{
|
||||
for (GenericParseContext* gpc = pc; gpc; gpc = gpc->parent) {
|
||||
SharedContext* sc = gpc->sc;
|
||||
if (sc->allowSuperProperty()) {
|
||||
if (sc->isFunctionBox())
|
||||
sc->asFunctionBox()->setNeedsHomeObject();
|
||||
return true;
|
||||
} else if (sc->isFunctionBox() && !sc->asFunctionBox()->function()->isArrow()) {
|
||||
// super is not legal is normal functions.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
|
||||
|
@ -7709,9 +7684,6 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
|
||||
JS_CHECK_RECURSION(context, return null());
|
||||
|
||||
bool isSuper = false;
|
||||
uint32_t superBegin;
|
||||
|
||||
/* Check for new expression first. */
|
||||
if (tt == TOK_NEW) {
|
||||
lhs = handler.newList(PNK_NEW, JSOP_NEW);
|
||||
|
@ -7736,10 +7708,6 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
if (isSpread)
|
||||
handler.setOp(lhs, JSOP_SPREADNEW);
|
||||
}
|
||||
} else if (tt == TOK_SUPER) {
|
||||
lhs = null();
|
||||
isSuper = true;
|
||||
superBegin = pos().begin;
|
||||
} else {
|
||||
lhs = primaryExpr(tt, invoked);
|
||||
if (!lhs)
|
||||
|
@ -7758,16 +7726,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
return null();
|
||||
if (tt == TOK_NAME) {
|
||||
PropertyName* field = tokenStream.currentName();
|
||||
if (isSuper) {
|
||||
isSuper = false;
|
||||
if (!checkAndMarkSuperScope()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property");
|
||||
return null();
|
||||
}
|
||||
nextMember = handler.newSuperProperty(field, TokenPos(superBegin, pos().end));
|
||||
} else {
|
||||
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
|
||||
}
|
||||
if (!nextMember)
|
||||
return null();
|
||||
} else {
|
||||
|
@ -7781,28 +7740,13 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
|
||||
|
||||
if (isSuper) {
|
||||
isSuper = false;
|
||||
if (!checkAndMarkSuperScope()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
|
||||
return null();
|
||||
}
|
||||
nextMember = handler.newSuperElement(propExpr, TokenPos(superBegin, pos().end));
|
||||
} else {
|
||||
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
|
||||
}
|
||||
if (!nextMember)
|
||||
return null();
|
||||
} else if ((allowCallSyntax && tt == TOK_LP) ||
|
||||
tt == TOK_TEMPLATE_HEAD ||
|
||||
tt == TOK_NO_SUBS_TEMPLATE)
|
||||
{
|
||||
if (isSuper) {
|
||||
// For now...
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPER);
|
||||
return null();
|
||||
}
|
||||
|
||||
JSOp op = JSOP_CALL;
|
||||
nextMember = handler.newList(tt == TOK_LP ? PNK_CALL : PNK_TAGGED_TEMPLATE, JSOP_CALL);
|
||||
if (!nextMember)
|
||||
|
@ -7821,12 +7765,6 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
*/
|
||||
if (pc->sc->isFunctionBox() && !pc->sc->strict())
|
||||
pc->sc->asFunctionBox()->setHasExtensibleScope();
|
||||
|
||||
// If we're in a method, mark the method as requiring
|
||||
// support for 'super', since direct eval code can use it.
|
||||
// (If we're not in a method, that's fine, so ignore the
|
||||
// return value.)
|
||||
checkAndMarkSuperScope();
|
||||
}
|
||||
} else if (JSAtom* atom = handler.isGetProp(lhs)) {
|
||||
/* Select JSOP_FUNAPPLY given foo.apply(...). */
|
||||
|
@ -7860,22 +7798,12 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
|||
}
|
||||
handler.setOp(nextMember, op);
|
||||
} else {
|
||||
if (isSuper) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPER);
|
||||
return null();
|
||||
}
|
||||
tokenStream.ungetToken();
|
||||
return lhs;
|
||||
}
|
||||
|
||||
lhs = nextMember;
|
||||
}
|
||||
|
||||
if (isSuper) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_SUPER);
|
||||
return null();
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
|
|
|
@ -575,8 +575,6 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
Node parenExprOrGeneratorComprehension();
|
||||
Node exprInParens();
|
||||
|
||||
bool checkAndMarkSuperScope();
|
||||
|
||||
bool methodDefinition(PropListType listType, Node propList, Node propname, FunctionType type,
|
||||
GeneratorKind generatorKind, bool isStatic, JSOp Op);
|
||||
|
||||
|
|
|
@ -126,16 +126,13 @@ class FunctionContextFlags
|
|||
//
|
||||
bool definitelyNeedsArgsObj:1;
|
||||
|
||||
bool needsHomeObject:1;
|
||||
|
||||
public:
|
||||
FunctionContextFlags()
|
||||
: mightAliasLocals(false),
|
||||
hasExtensibleScope(false),
|
||||
needsDeclEnvObject(false),
|
||||
argumentsHasLocalBinding(false),
|
||||
definitelyNeedsArgsObj(false),
|
||||
needsHomeObject(false)
|
||||
definitelyNeedsArgsObj(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -228,24 +225,17 @@ class SharedContext
|
|||
bool isDotVariable(JSAtom* atom) const {
|
||||
return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
|
||||
}
|
||||
|
||||
virtual bool allowSuperProperty() const = 0;
|
||||
};
|
||||
|
||||
class GlobalSharedContext : public SharedContext
|
||||
{
|
||||
private:
|
||||
bool allowSuperProperty_;
|
||||
|
||||
public:
|
||||
GlobalSharedContext(ExclusiveContext* cx,
|
||||
Directives directives, bool extraWarnings, bool allowSuperProperty)
|
||||
: SharedContext(cx, directives, extraWarnings),
|
||||
allowSuperProperty_(allowSuperProperty)
|
||||
Directives directives, bool extraWarnings)
|
||||
: SharedContext(cx, directives, extraWarnings)
|
||||
{}
|
||||
|
||||
ObjectBox* toObjectBox() { return nullptr; }
|
||||
bool allowSuperProperty() const { return allowSuperProperty_; }
|
||||
};
|
||||
|
||||
class FunctionBox : public ObjectBox, public SharedContext
|
||||
|
@ -298,7 +288,6 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
|
||||
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
||||
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
||||
bool needsHomeObject() const { return funCxFlags.needsHomeObject; }
|
||||
|
||||
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
|
@ -306,8 +295,6 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; }
|
||||
void setDefinitelyNeedsArgsObj() { MOZ_ASSERT(funCxFlags.argumentsHasLocalBinding);
|
||||
funCxFlags.definitelyNeedsArgsObj = true; }
|
||||
void setNeedsHomeObject() { MOZ_ASSERT(function()->isMethod());
|
||||
funCxFlags.needsHomeObject = true; }
|
||||
|
||||
bool hasDefaults() const {
|
||||
return length != function()->nargs() - function()->hasRest();
|
||||
|
@ -334,13 +321,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
return bindings.hasAnyAliasedBindings() ||
|
||||
hasExtensibleScope() ||
|
||||
needsDeclEnvObject() ||
|
||||
needsHomeObject() ||
|
||||
isGenerator();
|
||||
}
|
||||
|
||||
bool allowSuperProperty() const {
|
||||
return function()->isMethod();
|
||||
}
|
||||
};
|
||||
|
||||
inline FunctionBox*
|
||||
|
|
|
@ -187,8 +187,6 @@ class SyntaxParseHandler
|
|||
|
||||
Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
|
||||
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
||||
Node newSuperProperty(JSAtom* atom, const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newSuperElement(Node expr, const TokenPos& pos) { return NodeGeneric; }
|
||||
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
bool addShorthand(Node literal, Node name, Node expr) { return true; }
|
||||
|
|
|
@ -112,7 +112,6 @@
|
|||
macro(IMPORT, "keyword 'import'") \
|
||||
macro(CLASS, "keyword 'class'") \
|
||||
macro(EXTENDS, "keyword 'extends'") \
|
||||
macro(SUPER, "keyword 'super'") \
|
||||
macro(RESERVED, "reserved keyword") \
|
||||
/* reserved keywords in strict mode */ \
|
||||
macro(STRICT_RESERVED, "reserved keyword") \
|
||||
|
|
|
@ -1007,7 +1007,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||
#ifndef JS_HAS_CLASSES
|
||||
|| kw->tokentype == TOK_CLASS
|
||||
|| kw->tokentype == TOK_EXTENDS
|
||||
|| kw->tokentype == TOK_SUPER
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
|
|
@ -1759,8 +1759,7 @@ void
|
|||
CodeGenerator::emitLambdaInit(Register output, Register scopeChain,
|
||||
const LambdaFunctionInfo& info)
|
||||
{
|
||||
MOZ_ASSERT((info.fun->isArrow() || info.fun->isMethod()) ==
|
||||
!!(info.flags & JSFunction::EXTENDED));
|
||||
MOZ_ASSERT(info.fun->isArrow() == !!(info.flags & JSFunction::EXTENDED));
|
||||
|
||||
// Initialize nargs and flags. We do this with a single uint32 to avoid
|
||||
// 16-bit writes.
|
||||
|
|
|
@ -3947,7 +3947,7 @@ GetElementIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
|
|||
AutoDetectInvalidation adi(cx, res, ion);
|
||||
|
||||
if (cache.isDisabled()) {
|
||||
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, obj, idval, res))
|
||||
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, idval, res))
|
||||
return false;
|
||||
if (!cache.monitoredResult())
|
||||
TypeScript::Monitor(cx, script, pc, res);
|
||||
|
@ -3996,7 +3996,7 @@ GetElementIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
|
|||
}
|
||||
}
|
||||
|
||||
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, obj, idval, res))
|
||||
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, idval, res))
|
||||
return false;
|
||||
|
||||
// Disable cache when we reach max stubs or update failed too much.
|
||||
|
|
|
@ -205,8 +205,6 @@ MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id"
|
|||
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
|
||||
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
|
||||
MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
|
||||
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
|
||||
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
|
||||
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
|
||||
|
@ -489,6 +487,3 @@ MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elemen
|
|||
MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
|
||||
MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
|
||||
MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS, 0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
|
||||
|
||||
// Super
|
||||
MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
|
||||
|
|
|
@ -83,6 +83,7 @@ const char js_protected_str[] = "protected";
|
|||
const char js_public_str[] = "public";
|
||||
const char js_send_str[] = "send";
|
||||
const char js_setter_str[] = "setter";
|
||||
const char js_super_str[] = "super";
|
||||
const char js_switch_str[] = "switch";
|
||||
const char js_this_str[] = "this";
|
||||
const char js_try_str[] = "try";
|
||||
|
|
|
@ -115,7 +115,6 @@ class JSFunction : public js::NativeObject
|
|||
return nonLazyScript()->hasAnyAliasedBindings() ||
|
||||
nonLazyScript()->funHasExtensibleScope() ||
|
||||
nonLazyScript()->funNeedsDeclEnvObject() ||
|
||||
nonLazyScript()->needsHomeObject() ||
|
||||
isGenerator();
|
||||
}
|
||||
|
||||
|
@ -590,8 +589,6 @@ class FunctionExtended : public JSFunction
|
|||
/* Arrow functions store their lexical |this| in the first extended slot. */
|
||||
static const unsigned ARROW_THIS_SLOT = 0;
|
||||
|
||||
static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
|
||||
|
||||
static inline size_t offsetOfExtendedSlot(unsigned which) {
|
||||
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
|
||||
return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
|
||||
|
|
|
@ -1548,22 +1548,12 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc)
|
|||
quote(prop, '\'') &&
|
||||
write("]");
|
||||
}
|
||||
case JSOP_GETPROP_SUPER:
|
||||
{
|
||||
RootedAtom prop(cx, loadAtom(pc));
|
||||
return write("super.") &&
|
||||
quote(prop, '\0');
|
||||
}
|
||||
case JSOP_GETELEM:
|
||||
case JSOP_CALLELEM:
|
||||
return decompilePCForStackOperand(pc, -2) &&
|
||||
write("[") &&
|
||||
decompilePCForStackOperand(pc, -1) &&
|
||||
write("]");
|
||||
case JSOP_GETELEM_SUPER:
|
||||
return write("super[") &&
|
||||
decompilePCForStackOperand(pc, -3) &&
|
||||
write("]");
|
||||
case JSOP_NULL:
|
||||
return write(js_null_str);
|
||||
case JSOP_TRUE:
|
||||
|
|
|
@ -3020,16 +3020,6 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
builder.memberExpression(false, expr, id, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_SUPERPROP:
|
||||
{
|
||||
RootedValue superBase(cx), id(cx);
|
||||
RootedAtom superAtom(cx, cx->names().super);
|
||||
RootedAtom pnAtom(cx, pn->pn_atom);
|
||||
return identifier(superAtom, nullptr, &superBase) &&
|
||||
identifier(pnAtom, nullptr, &id) &&
|
||||
builder.memberExpression(false, superBase, id, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_ELEM:
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
|
||||
|
@ -3041,17 +3031,6 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
builder.memberExpression(true, left, right, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_SUPERELEM:
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
|
||||
|
||||
RootedValue superBase(cx), expr(cx);
|
||||
RootedAtom superAtom(cx, cx->names().super);
|
||||
return identifier(superAtom, nullptr, &superBase) &&
|
||||
expression(pn->pn_kid, &expr) &&
|
||||
builder.memberExpression(true, superBase, expr, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_CALLSITEOBJ:
|
||||
{
|
||||
NodeVector raw(cx);
|
||||
|
|
|
@ -2625,7 +2625,6 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
|
|||
script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
|
||||
script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false;
|
||||
script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false;
|
||||
script->needsHomeObject_ = funbox ? funbox->needsHomeObject() : false;
|
||||
script->hasSingletons_ = bce->hasSingletons;
|
||||
|
||||
if (funbox) {
|
||||
|
|
|
@ -1020,8 +1020,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
// keep it from relazifying.
|
||||
bool doNotRelazify_:1;
|
||||
|
||||
bool needsHomeObject_:1;
|
||||
|
||||
// Add padding so JSScript is gc::Cell aligned. Make padding protected
|
||||
// instead of private to suppress -Wunused-private-field compiler warnings.
|
||||
protected:
|
||||
|
@ -1286,14 +1284,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
void setNeedsHomeObject() {
|
||||
needsHomeObject_ = true;
|
||||
}
|
||||
bool needsHomeObject() const {
|
||||
return needsHomeObject_;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
* may not need to create an arguments object. This is determined by
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
var test = `
|
||||
|
||||
// Super property (and calls) works in non-extending classes and object
|
||||
// litterals.
|
||||
class toStringTest {
|
||||
constructor() {
|
||||
// Install a property to make it plausible that it's the same this
|
||||
this.foo = "rhinoceros";
|
||||
}
|
||||
|
||||
test() {
|
||||
assertEq(super.toSource(), super["toSource"]());
|
||||
assertEq(super.toSource(), this.toSource());
|
||||
}
|
||||
}
|
||||
|
||||
new toStringTest().test();
|
||||
|
||||
let toStrOL = {
|
||||
test() {
|
||||
assertEq(super.toSource(), super["toSource"]());
|
||||
assertEq(super.toSource(), this.toSource());
|
||||
}
|
||||
}
|
||||
|
||||
toStrOL.test();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,23 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class Base {
|
||||
constructor() {}
|
||||
}
|
||||
class Mid extends Base {
|
||||
constructor() {}
|
||||
f() { return new super.constructor(); }
|
||||
}
|
||||
class Derived extends Mid {
|
||||
constructor() {}
|
||||
}
|
||||
let d = new Derived();
|
||||
var df = d.f();
|
||||
assertEq(df.constructor, Base);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,65 +0,0 @@
|
|||
var test = `
|
||||
|
||||
// First, let's test the trivial. A chain of three works.
|
||||
class base {
|
||||
constructor() { }
|
||||
testChain() {
|
||||
this.baseCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
class middle extends base {
|
||||
constructor() { }
|
||||
testChain() {
|
||||
this.middleCalled = true;
|
||||
super.testChain();
|
||||
}
|
||||
}
|
||||
|
||||
class derived extends middle {
|
||||
constructor() { }
|
||||
testChain() {
|
||||
super.testChain();
|
||||
assertEq(this.middleCalled, true);
|
||||
assertEq(this.baseCalled, true);
|
||||
}
|
||||
}
|
||||
|
||||
new derived().testChain();
|
||||
|
||||
// Super even chains in a wellbehaved fashion with normal functions.
|
||||
function bootlegMiddle() { }
|
||||
bootlegMiddle.prototype = middle.prototype;
|
||||
|
||||
new class extends bootlegMiddle {
|
||||
constructor() { }
|
||||
testChain() {
|
||||
super.testChain();
|
||||
assertEq(this.middleCalled, true);
|
||||
assertEq(this.baseCalled, true);
|
||||
}
|
||||
}().testChain();
|
||||
|
||||
// Now let's try out some "long" chains
|
||||
base.prototype.x = "yeehaw";
|
||||
|
||||
let chain = class extends base { constructor() { } }
|
||||
|
||||
const CHAIN_LENGTH = 100;
|
||||
for (let i = 0; i < CHAIN_LENGTH; i++)
|
||||
chain = class extends chain { constructor() { } }
|
||||
|
||||
// Now we poke the chain
|
||||
let inst = new chain();
|
||||
inst.testChain();
|
||||
assertEq(inst.baseCalled, true);
|
||||
|
||||
assertEq(inst.x, "yeehaw");
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,32 +0,0 @@
|
|||
// Super property accesses should play nice with the pretty printer.
|
||||
|
||||
var test = `
|
||||
|
||||
function assertThrownErrorContains(thunk, substr) {
|
||||
try {
|
||||
thunk();
|
||||
throw new Error("Expected error containing " + substr + ", no exception thrown");
|
||||
} catch (e) {
|
||||
if (e.message.indexOf(substr) !== -1)
|
||||
return;
|
||||
throw new Error("Expected error containing " + substr + ", got " + e);
|
||||
}
|
||||
}
|
||||
|
||||
class testNonExistent {
|
||||
constructor() {
|
||||
super["prop"]();
|
||||
}
|
||||
}
|
||||
assertThrownErrorContains(() => new testNonExistent(), 'super["prop"]');
|
||||
|
||||
var ol = { testNonExistent() { super.prop(); } };
|
||||
assertThrownErrorContains(() => ol.testNonExistent(), "super.prop");
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,53 +0,0 @@
|
|||
var test = `
|
||||
|
||||
// |delete super.prop| and |delete super[expr]| throw universally.
|
||||
// Even so, we should make sure we get proper side effects
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
testDeleteProp() { delete super.prop; }
|
||||
testDeleteElem() {
|
||||
let sideEffect = 0;
|
||||
assertThrowsInstanceOf(() => delete super[sideEffect = 1], ReferenceError);
|
||||
assertEq(sideEffect, 1);
|
||||
}
|
||||
testDeleteElemPropValFirst() {
|
||||
// The deletion error is a reference error, but by munging the prototype
|
||||
// chain, we can force a typeerror from JSOP_SUPERBASE
|
||||
delete super[Object.setPrototypeOf(derived.prototype, null)];
|
||||
}
|
||||
}
|
||||
|
||||
var d = new derived();
|
||||
assertThrowsInstanceOf(() => d.testDeleteProp(), ReferenceError);
|
||||
d.testDeleteElem();
|
||||
assertThrowsInstanceOf(() => d.testDeleteElemPropValFirst(), TypeError);
|
||||
|
||||
// |delete super.x| does not delete anything before throwing.
|
||||
var thing1 = {
|
||||
go() { delete super.toString; }
|
||||
};
|
||||
let saved = Object.prototype.toString;
|
||||
assertThrowsInstanceOf(() => thing1.go(), ReferenceError);
|
||||
assertEq(Object.prototype.toString, saved);
|
||||
|
||||
// |delete super.x| does not tell the prototype to delete anything, when it's a proxy.
|
||||
var thing2 = {
|
||||
go() { delete super.prop; }
|
||||
};
|
||||
Object.setPrototypeOf(thing2, new Proxy({}, {
|
||||
deleteProperty(x) { throw "FAIL"; }
|
||||
}));
|
||||
assertThrowsInstanceOf(() => thing2.go(), ReferenceError);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,84 +0,0 @@
|
|||
var test = `
|
||||
|
||||
let derivedInstance;
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
method(a, b, c) {
|
||||
assertEq(this, derivedInstance);
|
||||
this.methodCalled = true;
|
||||
assertEq(a, 1);
|
||||
assertEq(b, 2);
|
||||
assertEq(c, 3);
|
||||
}
|
||||
|
||||
get prop() {
|
||||
assertEq(this, derivedInstance);
|
||||
this.getterCalled = true;
|
||||
return this._prop;
|
||||
}
|
||||
|
||||
set prop(val) {
|
||||
assertEq(this, derivedInstance);
|
||||
this.setterCalled = true;
|
||||
this._prop = val;
|
||||
}
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
|
||||
// |super| actually checks the chain, not |this|
|
||||
method() { throw "FAIL"; }
|
||||
get prop() { throw "FAIL"; }
|
||||
set prop(v) { throw "FAIL"; }
|
||||
|
||||
test() {
|
||||
this.reset();
|
||||
// While we're here. Let's check on super spread calls...
|
||||
let spread = [1,2,3];
|
||||
super.method(...spread);
|
||||
super.prop++;
|
||||
this.asserts();
|
||||
}
|
||||
|
||||
testInEval() {
|
||||
this.reset();
|
||||
eval("super.method(1,2,3); super.prop++");
|
||||
this.asserts();
|
||||
}
|
||||
|
||||
testInArrow() {
|
||||
this.reset();
|
||||
(() => (super.method(1,2,3), super.prop++))();
|
||||
this.asserts();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._prop = 0;
|
||||
this.methodCalled = false;
|
||||
this.setterCalled = false;
|
||||
this.getterCalled = false;
|
||||
}
|
||||
|
||||
asserts() {
|
||||
assertEq(this.methodCalled, true);
|
||||
assertEq(this.getterCalled, true);
|
||||
assertEq(this.setterCalled, true);
|
||||
assertEq(this._prop, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
derivedInstance = new derived();
|
||||
derivedInstance.test();
|
||||
derivedInstance.testInEval();
|
||||
derivedInstance.testInArrow();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,50 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
let seenValues;
|
||||
Object.defineProperty(base.prototype, "minutes",
|
||||
{
|
||||
set(x) {
|
||||
assertEq(x, 525600);
|
||||
seenValues.push(x);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(base.prototype, "intendent",
|
||||
{
|
||||
set(x) {
|
||||
assertEq(x, "Fred");
|
||||
seenValues.push(x)
|
||||
}
|
||||
});
|
||||
|
||||
const testArr = [525600, "Fred"];
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
prepForTest() { seenValues = []; }
|
||||
testAsserts() { assertDeepEq(seenValues, testArr); }
|
||||
testProps() {
|
||||
this.prepForTest();
|
||||
[super.minutes, super.intendent] = testArr;
|
||||
this.testAsserts();
|
||||
}
|
||||
testElems() {
|
||||
this.prepForTest();
|
||||
[super["minutes"], super["intendent"]] = testArr;
|
||||
this.testAsserts();
|
||||
}
|
||||
}
|
||||
|
||||
let d = new derived();
|
||||
d.testProps();
|
||||
d.testElems();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,33 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class testForIn {
|
||||
constructor() {
|
||||
let hits = 0;
|
||||
for (super.prop in { prop1: 1, prop2: 2 })
|
||||
hits++;
|
||||
assertEq(this.prop, "prop2");
|
||||
assertEq(hits, 2);
|
||||
}
|
||||
}
|
||||
|
||||
new testForIn();
|
||||
|
||||
|
||||
({
|
||||
testForOf() {
|
||||
let hits = 0;
|
||||
for (super["prop"] of [1, 2])
|
||||
hits++;
|
||||
assertEq(this.prop, 2);
|
||||
assertEq(hits, 2);
|
||||
}
|
||||
}).testForOf();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,68 +0,0 @@
|
|||
print("foo");
|
||||
var test = `
|
||||
|
||||
// This is super weird. A super property reference in the spec contains two
|
||||
// things. The first is the object to do the lookup on, the super base. This
|
||||
// should be unchanged, no matter what's going on: I can move the method to
|
||||
// another object. I can pull it out as its own function. I can put it on my
|
||||
// head and run around the front yard. No changes. The other half, the |this|
|
||||
// for invoked calls, is the this at the time of referencing the property, which
|
||||
// means it's gonna vary wildly as stuff gets moved around.
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
test(expectedThis) { assertEq(this, expectedThis); }
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
test(expected) { super.test(expected); }
|
||||
testArrow() { return (() => super.test(this)); }
|
||||
}
|
||||
|
||||
let derivedInstance = new derived();
|
||||
derivedInstance.test(derivedInstance);
|
||||
|
||||
let obj = { test: derivedInstance.test };
|
||||
obj.test(obj);
|
||||
|
||||
let test = derivedInstance.test;
|
||||
// Hah! The engine is not prepared for non-object receivers, since this couldn't
|
||||
// happen before. Hope Waldo fixes this soon as he claims he will :)
|
||||
assertThrowsInstanceOf(() =>test(undefined), TypeError);
|
||||
|
||||
let anotherObject = { };
|
||||
derivedInstance.test.call(anotherObject, anotherObject);
|
||||
|
||||
let strThis = "this is not an object!";
|
||||
derivedInstance.test.call(strThis, strThis);
|
||||
|
||||
// You can take the arrow function out of the super, ... or something like that
|
||||
let arrowTest = derivedInstance.testArrow();
|
||||
arrowTest();
|
||||
|
||||
// There's no magic "super script index" per code location.
|
||||
class base1 {
|
||||
constructor() { }
|
||||
test() { return "llama"; }
|
||||
}
|
||||
class base2 {
|
||||
constructor() { }
|
||||
test() { return "alpaca"; }
|
||||
}
|
||||
|
||||
let animals = [];
|
||||
for (let exprBase of [base1, base2])
|
||||
new class extends exprBase {
|
||||
constructor() { }
|
||||
test() { animals.push(super["test"]()); }
|
||||
}().test();
|
||||
assertDeepEq(animals, ["llama", "alpaca"]);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,100 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
method() { this.methodCalled++; }
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { this.methodCalled = 0; }
|
||||
|
||||
// Test orderings of various evaluations relative to the superbase
|
||||
|
||||
// Unlike in regular element evaluation, the propVal is evaluated before
|
||||
// checking the starting object ([[HomeObject]].[[Prototype]])
|
||||
testElem() { super[ruin()]; }
|
||||
|
||||
// The starting object for looking up super.method is determined before
|
||||
// ruin() is called.
|
||||
testProp() { super.method(ruin()); }
|
||||
|
||||
// The entire super.method property lookup has concluded before the args
|
||||
// are evaluated
|
||||
testPropCallDeleted() { super.method(()=>delete base.prototype.method); }
|
||||
|
||||
// The starting object for looking up super["prop"] is determined before
|
||||
// ruin() is called.
|
||||
testElemAssign() { super["prop"] = ruin(); }
|
||||
|
||||
// Test the normal assignment gotchas
|
||||
testAssignElemPropValChange() {
|
||||
let x = "prop1";
|
||||
super[x] = (()=>(x = "prop2", 0))();
|
||||
assertEq(this.prop1, 0);
|
||||
assertEq(this.prop2, undefined);
|
||||
}
|
||||
|
||||
testAssignProp() {
|
||||
Object.defineProperty(base.prototype, "piggy",
|
||||
{
|
||||
configurable: true,
|
||||
set() { throw "WEE WEE WEE WEE"; }
|
||||
});
|
||||
|
||||
// The property lookup is noted, but not actually evaluated, until the
|
||||
// right hand side is. Please don't make the piggy cry.
|
||||
super.piggy = (() => delete base.prototype.piggy)();
|
||||
}
|
||||
testCompoundAssignProp() {
|
||||
let getterCalled = false;
|
||||
Object.defineProperty(base.prototype, "horse",
|
||||
{
|
||||
configurable: true,
|
||||
get() { getterCalled = true; return "Of course"; },
|
||||
set() { throw "NO!"; }
|
||||
});
|
||||
super.horse += (()=>(delete base.prototype.horse, ", of course!"))();
|
||||
assertEq(getterCalled, true);
|
||||
|
||||
// So, is a horse a horse?
|
||||
assertEq(this.horse, "Of course, of course!");
|
||||
}
|
||||
}
|
||||
|
||||
function ruin() {
|
||||
Object.setPrototypeOf(derived.prototype, null);
|
||||
return 5;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
Object.setPrototypeOf(derived.prototype, base.prototype);
|
||||
}
|
||||
|
||||
let instance = new derived();
|
||||
|
||||
assertThrowsInstanceOf(() => instance.testElem(), TypeError);
|
||||
reset();
|
||||
|
||||
instance.testProp();
|
||||
assertEq(instance.methodCalled, 1);
|
||||
reset();
|
||||
|
||||
instance.testPropCallDeleted();
|
||||
assertEq(instance.methodCalled, 2);
|
||||
|
||||
instance.testElemAssign();
|
||||
assertEq(instance.prop, 5);
|
||||
reset();
|
||||
|
||||
instance.testAssignElemPropValChange();
|
||||
|
||||
instance.testAssignProp();
|
||||
|
||||
instance.testCompoundAssignProp();
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,29 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
test() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let standin = { test() { return true; } };
|
||||
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
test() {
|
||||
assertEq(super.test(), false);
|
||||
Object.setPrototypeOf(derived.prototype, standin);
|
||||
assertEq(super["test"](), true);
|
||||
}
|
||||
}
|
||||
|
||||
new derived().test();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,91 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
let midStaticHandler = { };
|
||||
|
||||
// We shouldn't use the |this.called| strategy here, since we have proxies
|
||||
// snooping property accesses.
|
||||
let getterCalled, setterCalled;
|
||||
|
||||
class mid extends new Proxy(base, midStaticHandler) {
|
||||
constructor() { }
|
||||
testSuperInProxy() {
|
||||
super.prop = "looking";
|
||||
assertEq(setterCalled, true);
|
||||
assertEq(super.prop, "found");
|
||||
assertEq(getterCalled, true);
|
||||
}
|
||||
}
|
||||
|
||||
class child extends mid {
|
||||
constructor() { }
|
||||
static testStaticLookups() {
|
||||
// This funtion is called more than once.
|
||||
this.called = false;
|
||||
super.prop;
|
||||
assertEq(this.called, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let midInstance = new mid();
|
||||
|
||||
// Make sure proxies are searched properly on the prototype chain
|
||||
let baseHandler = {
|
||||
get(target, p, receiver) {
|
||||
assertEq(receiver, midInstance);
|
||||
getterCalled = true;
|
||||
return "found";
|
||||
},
|
||||
|
||||
set(t,p,val,receiver) {
|
||||
assertEq(receiver, midInstance);
|
||||
assertEq(val, "looking");
|
||||
setterCalled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Object.setPrototypeOf(base.prototype, new Proxy(Object.prototype, baseHandler));
|
||||
|
||||
// make sure subclasses are not searched on static or super lookups.
|
||||
let childHandler = {
|
||||
get() { throw "NO!"; },
|
||||
set() { throw "NO!"; }
|
||||
}
|
||||
Object.setPrototypeOf(child.prototype, new Proxy(mid.prototype, childHandler));
|
||||
|
||||
midInstance.testSuperInProxy();
|
||||
|
||||
// Don't do this earlier to avoid the lookup of .prototype during class creation
|
||||
function midGet(target, p, receiver) {
|
||||
assertEq(receiver, child);
|
||||
receiver.called = true;
|
||||
}
|
||||
midStaticHandler.get = midGet;
|
||||
|
||||
child.testStaticLookups();
|
||||
|
||||
// Hey does super work in a proxy?
|
||||
assertEq(new Proxy(({ method() { return super.hasOwnProperty("method"); } }), {}).method(), true);
|
||||
|
||||
// What about a CCW?
|
||||
var g = newGlobal();
|
||||
var wrappedSuper = g.eval("({ method() { return super.hasOwnProperty('method'); } })");
|
||||
assertEq(wrappedSuper.method(), true);
|
||||
|
||||
// With a CCW on the proto chain?
|
||||
var wrappedBase = g.eval("({ method() { assertEq(this.__secretProp__, 42); return 14; } })");
|
||||
var unwrappedDerived = { __secretProp__: 42, method() { return super.method(); } }
|
||||
Object.setPrototypeOf(unwrappedDerived, wrappedBase);
|
||||
assertEq(unwrappedDerived.method(), 14);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,51 +0,0 @@
|
|||
var test = `
|
||||
|
||||
// Ensure that super lookups and sets skip over properties on the |this| object.
|
||||
// That is, super lookups start with the superclass, not the current class.
|
||||
|
||||
// The whole point: an empty superclass
|
||||
class base {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { this.prop = "flamingo"; }
|
||||
|
||||
toString() { throw "No!"; }
|
||||
|
||||
testSkipGet() {
|
||||
assertEq(super.prop, undefined);
|
||||
}
|
||||
|
||||
testSkipDerivedOverrides() {
|
||||
assertEq(super["toString"](), Object.prototype.toString.call(this));
|
||||
}
|
||||
|
||||
testSkipSet() {
|
||||
// since there's no prop on the chain, we should set the data property
|
||||
// on the receiver, |this|
|
||||
super.prop = "rat";
|
||||
assertEq(this.prop, "rat");
|
||||
|
||||
// Since the receiver is the instance, we can overwrite inherited
|
||||
// properties of the instance, even non-writable ones, as they could be
|
||||
// skipped in the super lookup.
|
||||
assertEq(this.nonWritableProp, "pony");
|
||||
super.nonWritableProp = "bear";
|
||||
assertEq(this.nonWritableProp, "bear");
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(derived.prototype, "nonWritableProp", { writable: false, value: "pony" });
|
||||
let instance = new derived();
|
||||
instance.testSkipGet();
|
||||
instance.testSkipDerivedOverrides();
|
||||
instance.testSkipSet();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,41 +0,0 @@
|
|||
var test = `
|
||||
|
||||
class base {
|
||||
constructor() { }
|
||||
static found() {
|
||||
this.foundCalled = true;
|
||||
}
|
||||
static get accessor() {
|
||||
assertEq(this, derived);
|
||||
return 45;
|
||||
}
|
||||
notFound() { }
|
||||
}
|
||||
|
||||
class derived extends base {
|
||||
constructor() { }
|
||||
|
||||
static found() { throw "NO!"; }
|
||||
static get accessor() { throw "NO!"; }
|
||||
|
||||
static test() {
|
||||
assertEq(super["notFound"], undefined);
|
||||
super.found();
|
||||
|
||||
// foundCalled is set on |derived| specifically.
|
||||
let calledDesc = Object.getOwnPropertyDescriptor(derived, "foundCalled");
|
||||
assertEq(calledDesc.value, true);
|
||||
|
||||
assertEq(super.accessor, 45);
|
||||
}
|
||||
}
|
||||
|
||||
derived.test();
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -1,30 +0,0 @@
|
|||
// While |super| is common in classes, it also works in object litterals,
|
||||
// where there is no guarantee of strict mode. Check that we do not somehow
|
||||
// get strict mode semantics when they were not called for
|
||||
|
||||
var test = `
|
||||
|
||||
// |undefined|, writable: false
|
||||
Object.defineProperty(Object.prototype, "prop", { writable: false });
|
||||
|
||||
class strictAssignmentTest {
|
||||
constructor() {
|
||||
// Strict mode. Throws.
|
||||
super.prop = 14;
|
||||
}
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(()=>new strictAssignmentTest(), TypeError);
|
||||
|
||||
// Non-strict. Silent failure.
|
||||
({ test() { super.prop = 14; } }).test();
|
||||
|
||||
assertEq(Object.prototype.prop, undefined);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
|
@ -123,13 +123,6 @@ function letStmt(head, body) {
|
|||
return Pattern({ type: "LetStatement", head: head, body: body });
|
||||
}
|
||||
|
||||
function superProp(id) {
|
||||
return dotExpr(ident("super"), id);
|
||||
}
|
||||
function superElem(id) {
|
||||
return memExpr(ident("super"), id);
|
||||
}
|
||||
|
||||
function classStmt(id, heritage, body) {
|
||||
return Pattern({ type: "ClassStatement",
|
||||
name: id,
|
||||
|
|
|
@ -11,18 +11,14 @@ function classesEnabled() {
|
|||
}
|
||||
|
||||
function testClasses() {
|
||||
function methodFun(id, kind, generator, args, body = []) {
|
||||
function simpleMethod(id, kind, generator, args=[], isStatic=false) {
|
||||
assertEq(generator && kind === "method", generator);
|
||||
let idN = ident(id);
|
||||
let methodMaker = generator ? genFunExpr : funExpr;
|
||||
let methodName = kind !== "method" ? null : idN;
|
||||
return methodMaker(methodName, args.map(ident), blockStmt(body));
|
||||
}
|
||||
let methodFun = methodMaker(methodName, args.map(ident), blockStmt([]));
|
||||
|
||||
function simpleMethod(id, kind, generator, args=[], isStatic=false) {
|
||||
return classMethod(ident(id),
|
||||
methodFun(id, kind, generator, args),
|
||||
kind, isStatic);
|
||||
return classMethod(idN, methodFun, kind, isStatic);
|
||||
}
|
||||
function emptyCPNMethod(id, isStatic) {
|
||||
return classMethod(computedName(lit(id)),
|
||||
|
@ -267,158 +263,6 @@ function testClasses() {
|
|||
assertClass("class NAME extends {}[foo] { constructor() { } }",
|
||||
[simpleConstructor], memExpr(objExpr([]), ident("foo")));
|
||||
|
||||
/* SuperProperty */
|
||||
// NOTE: Some of these tests involve object literals, as SuperProperty is a
|
||||
// valid production in any method definition, including in objectl
|
||||
// litterals. These tests still fall here, as |super| is not implemented in
|
||||
// any form without classes.
|
||||
|
||||
function assertValidSuperProps(assertion, makeStr, makeExpr, type, generator, args, static,
|
||||
extending)
|
||||
{
|
||||
let superProperty = superProp(ident("prop"));
|
||||
let superMember = superElem(lit("prop"));
|
||||
|
||||
let situations = [
|
||||
["super.prop", superProperty],
|
||||
["super['prop']", superMember],
|
||||
["super.prop()", callExpr(superProperty, [])],
|
||||
["super['prop']()", callExpr(superMember, [])],
|
||||
["new super.prop()", newExpr(superProperty, [])],
|
||||
["new super['prop']()", newExpr(superMember, [])],
|
||||
["delete super.prop", unExpr("delete", superProperty)],
|
||||
["delete super['prop']", unExpr("delete", superMember)],
|
||||
["++super.prop", updExpr("++", superProperty, true)],
|
||||
["super['prop']--", updExpr("--", superMember, false)],
|
||||
["super.prop = 4", aExpr("=", superProperty, lit(4))],
|
||||
["super['prop'] = 4", aExpr("=", superMember, lit(4))],
|
||||
["super.prop += 4", aExpr("+=", superProperty, lit(4))],
|
||||
["super['prop'] += 4", aExpr("+=", superMember, lit(4))],
|
||||
["super.prop -= 4", aExpr("-=", superProperty, lit(4))],
|
||||
["super['prop'] -= 4", aExpr("-=", superMember, lit(4))],
|
||||
["super.prop *= 4", aExpr("*=", superProperty, lit(4))],
|
||||
["super['prop'] *= 4", aExpr("*=", superMember, lit(4))],
|
||||
["super.prop /= 4", aExpr("/=", superProperty, lit(4))],
|
||||
["super['prop'] /= 4", aExpr("/=", superMember, lit(4))],
|
||||
["super.prop %= 4", aExpr("%=", superProperty, lit(4))],
|
||||
["super['prop'] %= 4", aExpr("%=", superMember, lit(4))],
|
||||
["super.prop <<= 4", aExpr("<<=", superProperty, lit(4))],
|
||||
["super['prop'] <<= 4", aExpr("<<=", superMember, lit(4))],
|
||||
["super.prop >>= 4", aExpr(">>=", superProperty, lit(4))],
|
||||
["super['prop'] >>= 4", aExpr(">>=", superMember, lit(4))],
|
||||
["super.prop >>>= 4", aExpr(">>>=", superProperty, lit(4))],
|
||||
["super['prop'] >>>= 4", aExpr(">>>=", superMember, lit(4))],
|
||||
["super.prop |= 4", aExpr("|=", superProperty, lit(4))],
|
||||
["super['prop'] |= 4", aExpr("|=", superMember, lit(4))],
|
||||
["super.prop ^= 4", aExpr("^=", superProperty, lit(4))],
|
||||
["super['prop'] ^= 4", aExpr("^=", superMember, lit(4))],
|
||||
["super.prop &= 4", aExpr("&=", superProperty, lit(4))],
|
||||
["super['prop'] &= 4", aExpr("&=", superMember, lit(4))],
|
||||
|
||||
// We can also use super from inside arrow functions in method
|
||||
// definitions
|
||||
["()=>super.prop", arrowExpr([], superProperty)],
|
||||
["()=>super['prop']", arrowExpr([], superMember)]];
|
||||
|
||||
for (let situation of situations) {
|
||||
let sitStr = situation[0];
|
||||
let sitExpr = situation[1];
|
||||
|
||||
let fun = methodFun("method", type, generator, args, [exprStmt(sitExpr)]);
|
||||
let str = makeStr(sitStr, type, generator, args, static, extending);
|
||||
assertion(str, makeExpr(fun, type, static), extending);
|
||||
}
|
||||
}
|
||||
|
||||
function assertValidSuperPropTypes(assertion, makeStr, makeExpr, static, extending) {
|
||||
for (let type of ["method", "get", "set"]) {
|
||||
if (type === "method") {
|
||||
// methods can also be generators
|
||||
assertValidSuperProps(assertion, makeStr, makeExpr, type, true, [], static, extending);
|
||||
assertValidSuperProps(assertion, makeStr, makeExpr, type, false, [], static, extending);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setters require 1 argument, and getters require 0
|
||||
assertValidSuperProps(assertion, makeStr, makeExpr, type, false,
|
||||
type === "set" ? ["x"] : [], static, extending);
|
||||
}
|
||||
}
|
||||
|
||||
function makeSuperPropMethodStr(propStr, type, generator, args) {
|
||||
return `${type === "method" ? "" : type} ${generator ? "*" : ""} method(${args.join(",")})
|
||||
{
|
||||
${propStr};
|
||||
}`;
|
||||
}
|
||||
|
||||
function makeClassSuperPropStr(propStr, type, generator, args, static, extending) {
|
||||
return `class NAME ${extending ? "extends null" : ""} {
|
||||
constructor() { };
|
||||
${static ? "static" : ""} ${makeSuperPropMethodStr(propStr, type, generator, args)}
|
||||
}`;
|
||||
}
|
||||
function makeClassSuperPropExpr(fun, type, static) {
|
||||
// We are going right into assertClass, so we don't have to build the
|
||||
// entire statement.
|
||||
return [simpleConstructor,
|
||||
classMethod(ident("method"), fun, type, static)];
|
||||
}
|
||||
function doClassSuperPropAssert(str, expr, extending) {
|
||||
assertClass(str, expr, extending ? lit(null) : null);
|
||||
}
|
||||
function assertValidClassSuperPropExtends(extending) {
|
||||
// super.prop and super[prop] are valid, regardless of whether the
|
||||
// method is static or not
|
||||
assertValidSuperPropTypes(doClassSuperPropAssert, makeClassSuperPropStr, makeClassSuperPropExpr,
|
||||
false, extending);
|
||||
assertValidSuperPropTypes(doClassSuperPropAssert, makeClassSuperPropStr, makeClassSuperPropExpr,
|
||||
true, extending);
|
||||
}
|
||||
function assertValidClassSuperProps() {
|
||||
// super.prop and super[prop] are valid, regardless of class heritage
|
||||
assertValidClassSuperPropExtends(false);
|
||||
assertValidClassSuperPropExtends(true);
|
||||
}
|
||||
|
||||
function makeOLSuperPropStr(propStr, type, generator, args) {
|
||||
let str = `({ ${makeSuperPropMethodStr(propStr, type, generator, args)} })`;
|
||||
return str;
|
||||
}
|
||||
function makeOLSuperPropExpr(fun) {
|
||||
return objExpr([{ type: "Property", key: ident("method"), value: fun}]);
|
||||
}
|
||||
function assertValidOLSuperProps() {
|
||||
assertValidSuperPropTypes(assertExpr, makeOLSuperPropStr, makeOLSuperPropExpr);
|
||||
}
|
||||
|
||||
|
||||
// Check all valid uses of SuperProperty
|
||||
assertValidClassSuperProps();
|
||||
assertValidOLSuperProps();
|
||||
|
||||
// SuperProperty is forbidden outside of method definitions.
|
||||
assertError("function foo () { super.prop; }", SyntaxError);
|
||||
assertError("(function () { super.prop; }", SyntaxError);
|
||||
assertError("(()=>super.prop)", SyntaxError);
|
||||
assertError("function *foo() { super.prop; }", SyntaxError);
|
||||
assertError("super.prop", SyntaxError);
|
||||
assertError("function foo () { super['prop']; }", SyntaxError);
|
||||
assertError("(function () { super['prop']; }", SyntaxError);
|
||||
assertError("(()=>super['prop'])", SyntaxError);
|
||||
assertError("function *foo() { super['prop']; }", SyntaxError);
|
||||
assertError("super['prop']", SyntaxError);
|
||||
|
||||
// Or inside functions inside method definitions...
|
||||
assertClassError("class NAME { constructor() { function nested() { super.prop; }}}", SyntaxError);
|
||||
|
||||
// Bare super is forbidden
|
||||
assertError("super", SyntaxError);
|
||||
|
||||
// Even where super is otherwise allowed
|
||||
assertError("{ foo() { super } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super; } }", SyntaxError);
|
||||
|
||||
/* EOF */
|
||||
// Clipped classes should throw a syntax error
|
||||
assertClassError("class NAME {", SyntaxError);
|
||||
|
@ -436,12 +280,7 @@ function testClasses() {
|
|||
assertClassError("class NAME { static get", SyntaxError);
|
||||
assertClassError("class NAME { static get y", SyntaxError);
|
||||
assertClassError("class NAME extends", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super.", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super.x", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super.m(", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super[", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { super(", SyntaxError);
|
||||
|
||||
}
|
||||
|
||||
if (classesEnabled())
|
||||
|
|
|
@ -191,7 +191,6 @@
|
|||
macro(strings, strings, "strings") \
|
||||
macro(StructType, StructType, "StructType") \
|
||||
macro(style, style, "style") \
|
||||
macro(super, super, "super") \
|
||||
macro(test, test, "test") \
|
||||
macro(throw, throw_, "throw") \
|
||||
macro(timestamp, timestamp, "timestamp") \
|
||||
|
|
|
@ -424,36 +424,35 @@ ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue ob
|
|||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleObject receiver,
|
||||
GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject receiver,
|
||||
HandleValue key, MutableHandleValue res)
|
||||
{
|
||||
MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM_SUPER);
|
||||
MOZ_ASSERT_IF(op == JSOP_GETELEM || op == JSOP_CALLELEM, obj == receiver);
|
||||
MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
|
||||
|
||||
do {
|
||||
uint32_t index;
|
||||
if (IsDefinitelyIndex(key, &index)) {
|
||||
if (GetElementNoGC(cx, obj, receiver, index, res.address()))
|
||||
if (GetElementNoGC(cx, receiver, receiver, index, res.address()))
|
||||
break;
|
||||
|
||||
if (!GetElement(cx, obj, receiver, index, res))
|
||||
if (!GetElement(cx, receiver, receiver, index, res))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsSymbolOrSymbolWrapper(key)) {
|
||||
RootedId id(cx, SYMBOL_TO_JSID(ToSymbolPrimitive(key)));
|
||||
if (!GetProperty(cx, obj, receiver, id, res))
|
||||
if (!GetProperty(cx, receiver, receiver, id, res))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (JSAtom* name = ToAtom<NoGC>(cx, key)) {
|
||||
if (name->isIndex(&index)) {
|
||||
if (GetElementNoGC(cx, obj, receiver, index, res.address()))
|
||||
if (GetElementNoGC(cx, receiver, receiver, index, res.address()))
|
||||
break;
|
||||
} else {
|
||||
if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), res.address()))
|
||||
if (GetPropertyNoGC(cx, receiver, receiver, name->asPropertyName(), res.address()))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -463,10 +462,10 @@ GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::Hand
|
|||
return false;
|
||||
|
||||
if (name->isIndex(&index)) {
|
||||
if (!GetElement(cx, obj, receiver, index, res))
|
||||
if (!GetElement(cx, receiver, receiver, index, res))
|
||||
return false;
|
||||
} else {
|
||||
if (!GetProperty(cx, obj, receiver, name->asPropertyName(), res))
|
||||
if (!GetProperty(cx, receiver, receiver, name->asPropertyName(), res))
|
||||
return false;
|
||||
}
|
||||
} while (false);
|
||||
|
@ -589,7 +588,7 @@ GetElementOperation(JSContext* cx, JSOp op, MutableHandleValue lref, HandleValue
|
|||
return GetPrimitiveElementOperation(cx, op, lref, rref, res);
|
||||
|
||||
RootedObject thisv(cx, &lref.toObject());
|
||||
return GetObjectElementOperation(cx, op, thisv, thisv, rref, res);
|
||||
return GetObjectElementOperation(cx, op, thisv, rref, res);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE JSString*
|
||||
|
|
|
@ -1604,12 +1604,9 @@ ModOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue
|
|||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
|
||||
const Value& value, bool strict, JSScript* script = nullptr,
|
||||
jsbytecode* pc = nullptr)
|
||||
SetObjectElementOperation(JSContext* cx, Handle<JSObject*> obj, HandleId id, const Value& value,
|
||||
bool strict, JSScript* script = nullptr, jsbytecode* pc = nullptr)
|
||||
{
|
||||
// receiver != obj happens only at super[expr], where we expect to find the property
|
||||
// People probably aren't building hashtables with |super| anyway.
|
||||
TypeScript::MonitorAssign(cx, obj, id);
|
||||
|
||||
if (obj->isNative() && JSID_IS_INT(id)) {
|
||||
|
@ -1626,9 +1623,7 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver,
|
|||
return false;
|
||||
|
||||
RootedValue tmp(cx, value);
|
||||
ObjectOpResult result;
|
||||
return SetProperty(cx, obj, id, tmp, receiver, result) &&
|
||||
result.checkStrictErrorOrWarning(cx, obj, id, strict);
|
||||
return PutProperty(cx, obj, id, tmp, strict);
|
||||
}
|
||||
|
||||
static MOZ_NEVER_INLINE bool
|
||||
|
@ -1891,10 +1886,18 @@ CASE(EnableInterruptsPseudoOpcode)
|
|||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_UNUSED2)
|
||||
CASE(JSOP_UNUSED92)
|
||||
CASE(JSOP_UNUSED103)
|
||||
CASE(JSOP_UNUSED104)
|
||||
CASE(JSOP_UNUSED105)
|
||||
CASE(JSOP_UNUSED107)
|
||||
CASE(JSOP_UNUSED125)
|
||||
CASE(JSOP_UNUSED126)
|
||||
CASE(JSOP_UNUSED148)
|
||||
CASE(JSOP_BACKPATCH)
|
||||
CASE(JSOP_UNUSED150)
|
||||
CASE(JSOP_UNUSED158)
|
||||
CASE(JSOP_UNUSED159)
|
||||
CASE(JSOP_UNUSED161)
|
||||
CASE(JSOP_UNUSED162)
|
||||
CASE(JSOP_UNUSED163)
|
||||
|
@ -2658,23 +2661,6 @@ CASE(JSOP_CALLPROP)
|
|||
}
|
||||
END_CASE(JSOP_GETPROP)
|
||||
|
||||
CASE(JSOP_GETPROP_SUPER)
|
||||
{
|
||||
RootedObject& receiver = rootObject0;
|
||||
RootedObject& obj = rootObject1;
|
||||
|
||||
FETCH_OBJECT(cx, -2, receiver);
|
||||
obj = ®S.sp[-1].toObject();
|
||||
|
||||
MutableHandleValue rref = REGS.stackHandleAt(-2);
|
||||
|
||||
if (!GetProperty(cx, obj, receiver, script->getName(REGS.pc), rref))
|
||||
goto error;
|
||||
|
||||
REGS.sp--;
|
||||
}
|
||||
END_CASE(JSOP_GETPROP_SUPER)
|
||||
|
||||
CASE(JSOP_GETXPROP)
|
||||
{
|
||||
RootedObject& obj = rootObject0;
|
||||
|
@ -2744,38 +2730,6 @@ CASE(JSOP_STRICTSETPROP)
|
|||
}
|
||||
END_CASE(JSOP_SETPROP)
|
||||
|
||||
CASE(JSOP_SETPROP_SUPER)
|
||||
CASE(JSOP_STRICTSETPROP_SUPER)
|
||||
{
|
||||
static_assert(JSOP_SETPROP_SUPER_LENGTH == JSOP_STRICTSETPROP_SUPER_LENGTH,
|
||||
"setprop-super and strictsetprop-super must be the same size");
|
||||
|
||||
|
||||
RootedValue& receiver = rootValue0;
|
||||
receiver = REGS.sp[-3];
|
||||
|
||||
RootedObject& obj = rootObject0;
|
||||
obj = ®S.sp[-2].toObject();
|
||||
|
||||
RootedValue& rval = rootValue1;
|
||||
rval = REGS.sp[-1];
|
||||
|
||||
RootedId& id = rootId0;
|
||||
id = NameToId(script->getName(REGS.pc));
|
||||
|
||||
ObjectOpResult result;
|
||||
if (!SetProperty(cx, obj, id, rval, receiver, result))
|
||||
goto error;
|
||||
|
||||
bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETPROP_SUPER;
|
||||
if (!result.checkStrictErrorOrWarning(cx, obj, id, strict))
|
||||
goto error;
|
||||
|
||||
REGS.sp[-3] = REGS.sp[-1];
|
||||
REGS.sp -= 2;
|
||||
}
|
||||
END_CASE(JSOP_SETPROP_SUPER)
|
||||
|
||||
CASE(JSOP_GETELEM)
|
||||
CASE(JSOP_CALLELEM)
|
||||
{
|
||||
|
@ -2797,27 +2751,6 @@ CASE(JSOP_CALLELEM)
|
|||
}
|
||||
END_CASE(JSOP_GETELEM)
|
||||
|
||||
CASE(JSOP_GETELEM_SUPER)
|
||||
{
|
||||
HandleValue rval = REGS.stackHandleAt(-3);
|
||||
RootedObject& receiver = rootObject0;
|
||||
FETCH_OBJECT(cx, -2, receiver);
|
||||
RootedObject& obj = rootObject1;
|
||||
obj = ®S.sp[-1].toObject();
|
||||
|
||||
MutableHandleValue res = REGS.stackHandleAt(-3);
|
||||
|
||||
// Since we have asserted that obj has to be an object, it cannot be
|
||||
// either optimized arguments, or indeed any primitive. This simplifies
|
||||
// our task some.
|
||||
if (!GetObjectElementOperation(cx, JSOp(*REGS.pc), obj, receiver, rval, res))
|
||||
goto error;
|
||||
|
||||
TypeScript::Monitor(cx, script, REGS.pc, res);
|
||||
REGS.sp -= 2;
|
||||
}
|
||||
END_CASE(JSOP_GETELEM_SUPER)
|
||||
|
||||
CASE(JSOP_SETELEM)
|
||||
CASE(JSOP_STRICTSETELEM)
|
||||
{
|
||||
|
@ -2828,37 +2761,13 @@ CASE(JSOP_STRICTSETELEM)
|
|||
RootedId& id = rootId0;
|
||||
FETCH_ELEMENT_ID(-2, id);
|
||||
Value& value = REGS.sp[-1];
|
||||
RootedValue& receiver = rootValue0;
|
||||
receiver = ObjectValue(*obj);
|
||||
if (!SetObjectElementOperation(cx, obj, receiver, id, value, *REGS.pc == JSOP_STRICTSETELEM))
|
||||
if (!SetObjectElementOperation(cx, obj, id, value, *REGS.pc == JSOP_STRICTSETELEM))
|
||||
goto error;
|
||||
REGS.sp[-3] = value;
|
||||
REGS.sp -= 2;
|
||||
}
|
||||
END_CASE(JSOP_SETELEM)
|
||||
|
||||
CASE(JSOP_SETELEM_SUPER)
|
||||
CASE(JSOP_STRICTSETELEM_SUPER)
|
||||
{
|
||||
static_assert(JSOP_SETELEM_SUPER_LENGTH == JSOP_STRICTSETELEM_SUPER_LENGTH,
|
||||
"setelem-super and strictsetelem-super must be the same size");
|
||||
|
||||
RootedId& id = rootId0;
|
||||
FETCH_ELEMENT_ID(-4, id);
|
||||
RootedValue& receiver = rootValue0;
|
||||
receiver = REGS.sp[-3];
|
||||
RootedObject& obj = rootObject1;
|
||||
obj = ®S.sp[-2].toObject();
|
||||
Value& value = REGS.sp[-1];
|
||||
|
||||
bool strict = JSOp(*REGS.pc) == JSOP_STRICTSETELEM_SUPER;
|
||||
if (!SetObjectElementOperation(cx, obj, receiver, id, value, strict))
|
||||
goto error;
|
||||
REGS.sp[-4] = value;
|
||||
REGS.sp -= 3;
|
||||
}
|
||||
END_CASE(JSOP_SETELEM_SUPER)
|
||||
|
||||
CASE(JSOP_EVAL)
|
||||
CASE(JSOP_STRICTEVAL)
|
||||
{
|
||||
|
@ -3909,8 +3818,8 @@ CASE(JSOP_CLASSHERITAGE)
|
|||
}
|
||||
}
|
||||
|
||||
REGS.sp[-1].setObject(*funcProto);
|
||||
PUSH_COPY(objProto);
|
||||
REGS.sp[-1] = objProto;
|
||||
PUSH_OBJECT(*funcProto);
|
||||
}
|
||||
END_CASE(JSOP_CLASSHERITAGE)
|
||||
|
||||
|
@ -3945,55 +3854,6 @@ CASE(JSOP_OBJWITHPROTO)
|
|||
}
|
||||
END_CASE(JSOP_OBJWITHPROTO)
|
||||
|
||||
CASE(JSOP_INITHOMEOBJECT)
|
||||
{
|
||||
MOZ_ASSERT(REGS.stackDepth() >= 2);
|
||||
|
||||
/* Load the function to be initialized */
|
||||
RootedFunction& func = rootFunction0;
|
||||
func = ®S.sp[-1].toObject().as<JSFunction>();
|
||||
MOZ_ASSERT(func->isMethod());
|
||||
|
||||
/* Load the home object */
|
||||
RootedNativeObject& obj = rootNativeObject0;
|
||||
obj = ®S.sp[-2].toObject().as<NativeObject>();
|
||||
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>());
|
||||
|
||||
func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj));
|
||||
}
|
||||
END_CASE(JSOP_INITHOMEOBJECT)
|
||||
|
||||
CASE(JSOP_SUPERBASE)
|
||||
{
|
||||
ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc));
|
||||
for (; !si.done(); ++si) {
|
||||
if (si.hasScopeObject() && si.type() == ScopeIter::Call) {
|
||||
JSFunction& callee = si.scope().as<CallObject>().callee();
|
||||
MOZ_ASSERT(callee.isMethod());
|
||||
MOZ_ASSERT(callee.nonLazyScript()->needsHomeObject());
|
||||
const Value& homeObjVal = callee.getExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT);
|
||||
|
||||
RootedObject& homeObj = rootObject0;
|
||||
homeObj = &homeObjVal.toObject();
|
||||
|
||||
RootedObject& superBase = rootObject1;
|
||||
if (!GetPrototype(cx, homeObj, &superBase))
|
||||
goto error;
|
||||
|
||||
if (!superBase) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
|
||||
"null", "object");
|
||||
goto error;
|
||||
}
|
||||
PUSH_OBJECT(*superBase);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (si.done())
|
||||
MOZ_CRASH("Unexpected scope chain in superbase");
|
||||
}
|
||||
END_CASE(JSOP_SUPERBASE)
|
||||
|
||||
DEFAULT()
|
||||
{
|
||||
char numBuf[12];
|
||||
|
@ -4369,8 +4229,7 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
|
|||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, index, &id))
|
||||
return false;
|
||||
RootedValue receiver(cx, ObjectValue(*obj));
|
||||
return SetObjectElementOperation(cx, obj, receiver, id, value, strict);
|
||||
return SetObjectElementOperation(cx, obj, id, value, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -4381,8 +4240,7 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
|
|||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, index, &id))
|
||||
return false;
|
||||
RootedValue receiver(cx, ObjectValue(*obj));
|
||||
return SetObjectElementOperation(cx, obj, receiver, id, value, strict, script, pc);
|
||||
return SetObjectElementOperation(cx, obj, id, value, strict, script, pc);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -45,9 +45,9 @@
|
|||
macro(export, export, TOK_EXPORT, JSVERSION_DEFAULT) \
|
||||
macro(class, class_, TOK_CLASS, JSVERSION_DEFAULT) \
|
||||
macro(extends, extends, TOK_EXTENDS, JSVERSION_DEFAULT) \
|
||||
macro(super, super, TOK_SUPER, JSVERSION_DEFAULT) \
|
||||
/* Reserved keywords. */ \
|
||||
macro(enum, enum_, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(super, super, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
/* Future reserved keywords, but only in strict mode. */ \
|
||||
macro(implements, implements, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(interface, interface, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
|
|
|
@ -486,7 +486,7 @@
|
|||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: heritage => funcProto, objProto
|
||||
* Stack: heritage => objProto, funcProto
|
||||
*/ \
|
||||
macro(JSOP_CLASSHERITAGE, 51, "classheritage", NULL, 1, 1, 2, JOF_BYTE) \
|
||||
/*
|
||||
|
@ -862,13 +862,7 @@
|
|||
*/ \
|
||||
macro(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, JOF_OBJECT) \
|
||||
\
|
||||
/*
|
||||
* Initialize the home object for functions with super bindings.
|
||||
*
|
||||
* This opcode takes the function and the object to be the home object, does
|
||||
* the set, and leaves both on the stack.
|
||||
*/\
|
||||
macro(JSOP_INITHOMEOBJECT, 92, "inithomeobject", NULL, 1, 2, 2, JOF_BYTE|JOF_SET|JOF_DETECTING) \
|
||||
macro(JSOP_UNUSED92, 92, "unused92", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Initialize a named property in an object literal, like '{a: x}'.
|
||||
|
@ -988,26 +982,9 @@
|
|||
*/ \
|
||||
macro(JSOP_NEWARRAY_COPYONWRITE, 102, "newarray_copyonwrite", NULL, 5, 0, 1, JOF_OBJECT) \
|
||||
\
|
||||
macro(JSOP_SUPERBASE, 103, "superbase", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pops the top two values, and pushes the property of one, using the other
|
||||
* as the receiver.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t nameIndex
|
||||
* Stack: receiver obj => obj[name]
|
||||
*/ \
|
||||
macro(JSOP_GETPROP_SUPER, 104, "getprop-super", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP) \
|
||||
/*
|
||||
* Pops the top three values on the stack as 'val' and 'obj', and 'receiver',
|
||||
* and performs 'obj.prop = val', pushing 'val' back onto the stack.
|
||||
* Throws a TypeError if the set-operation failed (per strict mode semantics).
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t nameIndex
|
||||
* Stack: receiver, obj, val => val
|
||||
*/ \
|
||||
macro(JSOP_STRICTSETPROP_SUPER, 105, "strictsetprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
|
||||
macro(JSOP_UNUSED103, 103, "unused103", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED104, 104, "unused104", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED105, 105, "unused105", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* This opcode precedes every labeled statement. It's a no-op.
|
||||
|
@ -1021,15 +998,7 @@
|
|||
*/ \
|
||||
macro(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, JOF_JUMP) \
|
||||
\
|
||||
/*
|
||||
* Pops the top three values on the stack as 'val', 'obj' and 'receiver',
|
||||
* and performs 'obj.prop = val', pushing 'val' back onto the stack.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t nameIndex
|
||||
* Stack: receiver, obj, val => val
|
||||
*/ \
|
||||
macro(JSOP_SETPROP_SUPER, 107, "setprop-super", NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
|
||||
macro(JSOP_UNUSED107, 107,"unused107", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Invokes 'callee' with 'this' and 'args', pushes return value onto the
|
||||
|
@ -1250,15 +1219,7 @@
|
|||
* nuses: (argc+2)
|
||||
*/ \
|
||||
macro(JSOP_STRICTEVAL, 124, "strict-eval", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSTRICT) \
|
||||
/*
|
||||
* LIKE JSOP_GETELEM but takes receiver on stack, and the propval is
|
||||
* evaluated before the obj.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: receiver, obj, propval => obj[propval]
|
||||
*/ \
|
||||
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
|
||||
macro(JSOP_UNUSED125, 125, "unused125", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
|
@ -1588,24 +1549,8 @@
|
|||
* Stack: => this
|
||||
*/ \
|
||||
macro(JSOP_GIMPLICITTHIS, 157, "gimplicitthis", "", 5, 0, 1, JOF_ATOM) \
|
||||
/*
|
||||
* LIKE JSOP_SETELEM, but takes receiver on the stack, and the propval is
|
||||
* evaluated before the base.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: propval, receiver, obj, val => val
|
||||
*/ \
|
||||
macro(JSOP_SETELEM_SUPER, 158, "setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSLOPPY) \
|
||||
/*
|
||||
* LIKE JSOP_STRICTSETELEM, but takes receiver on the stack, and the
|
||||
* propval is evaluated before the base.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: propval, receiver, obj, val => val
|
||||
*/ \
|
||||
macro(JSOP_STRICTSETELEM_SUPER, 159, "strict-setelem-super", NULL, 1, 4, 1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING|JOF_CHECKSTRICT) \
|
||||
macro(JSOP_UNUSED158, 158, "unused158", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED159, 159, "unused159", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Pushes a regular expression literal onto the stack.
|
||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 275;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 274;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 395,
|
||||
static_assert(JSErr_Limit == 392,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
|
Загрузка…
Ссылка в новой задаче