Bug 1141862 - Part 6: Implement ES6 SuperProperty and SuperMember. (r=jorendorff)

This commit is contained in:
Eric Faust 2015-04-08 17:41:01 -07:00
Родитель 72ad334118
Коммит 2571dab641
47 изменённых файлов: 1747 добавлений и 70 удалений

Просмотреть файл

@ -281,10 +281,12 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
if (!parser.checkOptions())
return nullptr;
Directives directives(options.strictOption);
GlobalSharedContext globalsc(cx, directives, options.extraWarningsOption);
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
bool allowSuperProperty = savedCallerFun && evalCaller->functionOrCallerFunction()->isMethod();
Directives directives(options.strictOption);
GlobalSharedContext globalsc(cx, directives, options.extraWarningsOption, allowSuperProperty);
Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
options, staticLevel, sourceObject, 0,
srcBuf.length()));

Просмотреть файл

@ -1991,6 +1991,7 @@ 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;
@ -2053,7 +2054,8 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
return checkSideEffects(pn->maybeExpr(), answer);
case PN_NULLARY:
if (pn->isKind(PNK_DEBUGGER))
if (pn->isKind(PNK_DEBUGGER) ||
pn->isKind(PNK_SUPERPROP))
*answer = true;
return true;
}
@ -2322,6 +2324,18 @@ 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)
{
@ -2342,10 +2356,25 @@ 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->getKind() == PNK_DOT);
MOZ_ASSERT(pn->pn_kid->isKind(PNK_DOT));
bool post;
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
@ -2381,6 +2410,50 @@ 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)
{
@ -2423,18 +2496,61 @@ 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)
{
@ -2451,10 +2567,30 @@ 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->getKind() == PNK_ELEM);
MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM));
if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM))
return false;
@ -2500,6 +2636,55 @@ 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)
{
@ -3277,17 +3462,40 @@ 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))
@ -3892,6 +4100,13 @@ 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))
@ -3900,6 +4115,11 @@ 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;
@ -3963,12 +4183,28 @@ 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
@ -4032,6 +4268,13 @@ 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);
@ -4043,6 +4286,13 @@ 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))
@ -5899,6 +6149,15 @@ 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;
@ -5906,6 +6165,21 @@ 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:
{
/*
@ -6069,6 +6343,10 @@ 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;
@ -6077,6 +6355,10 @@ 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
@ -6220,10 +6502,18 @@ 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))
@ -6450,6 +6740,14 @@ 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) {
@ -6777,28 +7075,39 @@ 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 (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))
if (constructor->pn_funbox->needsHomeObject()) {
if (!emit1(JSOP_INITHOMEOBJECT))
return false;
}
if (!emit1(JSOP_SWAP))
return false;
if (!emit1(JSOP_DUP2))
return false;
if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP))
@ -7144,10 +7453,20 @@ 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,6 +590,13 @@ 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,6 +419,8 @@ 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,6 +290,12 @@ 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,6 +214,7 @@ 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");
@ -237,6 +238,7 @@ 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,6 +154,8 @@ class UpvarCookie
F(CLASSMETHOD) \
F(CLASSMETHODLIST) \
F(CLASSNAMES) \
F(SUPERPROP) \
F(SUPERELEM) \
\
/* Unary operators. */ \
F(TYPEOF) \
@ -1425,6 +1427,37 @@ 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,7 +698,8 @@ 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);
GlobalSharedContext globalsc(context, directives, options().extraWarningsOption,
/* allowSuperProperty = */ false);
ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
&globalsc, /* newDirectives = */ nullptr,
/* staticLevel = */ 0, /* bodyid = */ 0,
@ -1229,7 +1230,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, Hand
}
gc::AllocKind allocKind = JSFunction::FinalizeKind;
if (kind == Arrow)
if (kind == Arrow || kind == Method)
allocKind = JSFunction::ExtendedFinalizeKind;
fun = NewFunctionWithProto(context, nullptr, 0, flags, NullPtr(), atom, proto,
allocKind, TenuredObject);
@ -4575,7 +4576,9 @@ 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;
@ -6321,6 +6324,8 @@ Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentF
case PNK_DOT:
case PNK_ELEM:
case PNK_SUPERPROP:
case PNK_SUPERELEM:
break;
case PNK_ARRAY:
@ -6489,6 +6494,8 @@ 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) ||
@ -7674,6 +7681,24 @@ 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)
@ -7684,6 +7709,9 @@ 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);
@ -7708,6 +7736,10 @@ 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)
@ -7726,7 +7758,16 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
return null();
if (tt == TOK_NAME) {
PropertyName* field = tokenStream.currentName();
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
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 {
@ -7740,13 +7781,28 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
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)
@ -7765,6 +7821,12 @@ 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(...). */
@ -7798,12 +7860,22 @@ 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,6 +575,8 @@ 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,13 +126,16 @@ class FunctionContextFlags
//
bool definitelyNeedsArgsObj:1;
bool needsHomeObject:1;
public:
FunctionContextFlags()
: mightAliasLocals(false),
hasExtensibleScope(false),
needsDeclEnvObject(false),
argumentsHasLocalBinding(false),
definitelyNeedsArgsObj(false)
definitelyNeedsArgsObj(false),
needsHomeObject(false)
{ }
};
@ -225,17 +228,24 @@ 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)
: SharedContext(cx, directives, extraWarnings)
Directives directives, bool extraWarnings, bool allowSuperProperty)
: SharedContext(cx, directives, extraWarnings),
allowSuperProperty_(allowSuperProperty)
{}
ObjectBox* toObjectBox() { return nullptr; }
bool allowSuperProperty() const { return allowSuperProperty_; }
};
class FunctionBox : public ObjectBox, public SharedContext
@ -288,6 +298,7 @@ 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; }
@ -295,6 +306,8 @@ 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();
@ -321,8 +334,13 @@ class FunctionBox : public ObjectBox, public SharedContext
return bindings.hasAnyAliasedBindings() ||
hasExtensibleScope() ||
needsDeclEnvObject() ||
needsHomeObject() ||
isGenerator();
}
bool allowSuperProperty() const {
return function()->isMethod();
}
};
inline FunctionBox*

Просмотреть файл

@ -187,6 +187,8 @@ 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,6 +112,7 @@
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,6 +1007,7 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
#ifndef JS_HAS_CLASSES
|| kw->tokentype == TOK_CLASS
|| kw->tokentype == TOK_EXTENDS
|| kw->tokentype == TOK_SUPER
#endif
)
{

Просмотреть файл

@ -735,9 +735,12 @@ js::Nursery::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind
MOZ_ALWAYS_INLINE size_t
js::Nursery::moveElementsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
{
fprintf(stderr, "dst: %p src: %p\n", dst, src);
if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
return 0;
fprintf(stderr, "got here: dst: %p src: %p\n", dst, src);
Zone* zone = src->zone();
ObjectElements* srcHeader = src->getElementsHeader();
ObjectElements* dstHeader;

Просмотреть файл

@ -1712,6 +1712,13 @@ CodeGenerator::visitLambda(LLambda* lir)
emitLambdaInit(output, scopeChain, info);
if (info.flags & JSFunction::EXTENDED) {
MOZ_ASSERT(info.fun->isMethod());
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
}
masm.bind(ool->rejoin());
}
@ -1759,8 +1766,6 @@ void
CodeGenerator::emitLambdaInit(Register output, Register scopeChain,
const LambdaFunctionInfo& info)
{
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.
union {

Просмотреть файл

@ -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, idval, res))
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, 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, idval, res))
if (!GetObjectElementOperation(cx, JSOp(*pc), obj, obj, idval, res))
return false;
// Disable cache when we reach max stubs or update failed too much.

Просмотреть файл

@ -205,6 +205,8 @@ 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")
@ -487,3 +489,6 @@ 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,7 +83,6 @@ 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";

Просмотреть файл

@ -721,6 +721,8 @@ inline void
JSFunction::trace(JSTracer* trc)
{
if (isExtended()) {
if (isMethod())
fprintf(stderr, "It's a method! %p\n", this);
TraceRange(trc, ArrayLength(toExtended()->extendedSlots),
(HeapValue*)toExtended()->extendedSlots, "nativeReserved");
}

Просмотреть файл

@ -115,6 +115,7 @@ class JSFunction : public js::NativeObject
return nonLazyScript()->hasAnyAliasedBindings() ||
nonLazyScript()->funHasExtensibleScope() ||
nonLazyScript()->funNeedsDeclEnvObject() ||
nonLazyScript()->needsHomeObject() ||
isGenerator();
}
@ -589,6 +590,8 @@ 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,12 +1548,22 @@ 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,6 +3020,16 @@ 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));
@ -3031,6 +3041,17 @@ 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,6 +2625,7 @@ 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,6 +1020,8 @@ 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:
@ -1284,6 +1286,14 @@ 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

Просмотреть файл

@ -0,0 +1,34 @@
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");

Просмотреть файл

@ -0,0 +1,23 @@
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");

Просмотреть файл

@ -0,0 +1,65 @@
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");

Просмотреть файл

@ -0,0 +1,32 @@
// 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");

Просмотреть файл

@ -0,0 +1,53 @@
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");

Просмотреть файл

@ -0,0 +1,84 @@
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");

Просмотреть файл

@ -0,0 +1,50 @@
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");

Просмотреть файл

@ -0,0 +1,33 @@
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");

Просмотреть файл

@ -0,0 +1,68 @@
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");

Просмотреть файл

@ -0,0 +1,100 @@
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");

Просмотреть файл

@ -0,0 +1,29 @@
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");

Просмотреть файл

@ -0,0 +1,91 @@
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() { return this.__secretProp__; } })");
var unwrappedDerived = { __secretProp__: 42, method() { return super.method(); } }
Object.setPrototypeOf(unwrappedDerived, wrappedBase);
assertEq(unwrappedDerived.method(), 42);
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");

Просмотреть файл

@ -0,0 +1,51 @@
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");

Просмотреть файл

@ -0,0 +1,41 @@
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");

Просмотреть файл

@ -0,0 +1,30 @@
// 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,6 +123,13 @@ 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,14 +11,18 @@ function classesEnabled() {
}
function testClasses() {
function simpleMethod(id, kind, generator, args=[], isStatic=false) {
function methodFun(id, kind, generator, args, body = []) {
assertEq(generator && kind === "method", generator);
let idN = ident(id);
let methodMaker = generator ? genFunExpr : funExpr;
let methodName = kind !== "method" ? null : idN;
let methodFun = methodMaker(methodName, args.map(ident), blockStmt([]));
return methodMaker(methodName, args.map(ident), blockStmt(body));
}
return classMethod(idN, methodFun, kind, isStatic);
function simpleMethod(id, kind, generator, args=[], isStatic=false) {
return classMethod(ident(id),
methodFun(id, kind, generator, args),
kind, isStatic);
}
function emptyCPNMethod(id, isStatic) {
return classMethod(computedName(lit(id)),
@ -263,6 +267,158 @@ 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);
@ -280,7 +436,12 @@ 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,6 +191,7 @@
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,35 +424,36 @@ ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue ob
}
static MOZ_ALWAYS_INLINE bool
GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject receiver,
GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject obj, JS::HandleObject receiver,
HandleValue key, MutableHandleValue res)
{
MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM || op == JSOP_GETELEM_SUPER);
MOZ_ASSERT_IF(op == JSOP_GETELEM || op == JSOP_CALLELEM, obj == receiver);
do {
uint32_t index;
if (IsDefinitelyIndex(key, &index)) {
if (GetElementNoGC(cx, receiver, receiver, index, res.address()))
if (GetElementNoGC(cx, obj, receiver, index, res.address()))
break;
if (!GetElement(cx, receiver, receiver, index, res))
if (!GetElement(cx, obj, receiver, index, res))
return false;
break;
}
if (IsSymbolOrSymbolWrapper(key)) {
RootedId id(cx, SYMBOL_TO_JSID(ToSymbolPrimitive(key)));
if (!GetProperty(cx, receiver, receiver, id, res))
if (!GetProperty(cx, obj, receiver, id, res))
return false;
break;
}
if (JSAtom* name = ToAtom<NoGC>(cx, key)) {
if (name->isIndex(&index)) {
if (GetElementNoGC(cx, receiver, receiver, index, res.address()))
if (GetElementNoGC(cx, obj, receiver, index, res.address()))
break;
} else {
if (GetPropertyNoGC(cx, receiver, receiver, name->asPropertyName(), res.address()))
if (GetPropertyNoGC(cx, obj, receiver, name->asPropertyName(), res.address()))
break;
}
}
@ -462,10 +463,10 @@ GetObjectElementOperation(JSContext* cx, JSOp op, JS::HandleObject receiver,
return false;
if (name->isIndex(&index)) {
if (!GetElement(cx, receiver, receiver, index, res))
if (!GetElement(cx, obj, receiver, index, res))
return false;
} else {
if (!GetProperty(cx, receiver, receiver, name->asPropertyName(), res))
if (!GetProperty(cx, obj, receiver, name->asPropertyName(), res))
return false;
}
} while (false);
@ -588,7 +589,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, rref, res);
return GetObjectElementOperation(cx, op, thisv, thisv, rref, res);
}
static MOZ_ALWAYS_INLINE JSString*

Просмотреть файл

@ -1604,9 +1604,12 @@ ModOperation(JSContext* cx, HandleValue lhs, HandleValue rhs, MutableHandleValue
}
static MOZ_ALWAYS_INLINE bool
SetObjectElementOperation(JSContext* cx, Handle<JSObject*> obj, HandleId id, const Value& value,
bool strict, JSScript* script = nullptr, jsbytecode* pc = nullptr)
SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleValue receiver, 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)) {
@ -1623,7 +1626,9 @@ SetObjectElementOperation(JSContext* cx, Handle<JSObject*> obj, HandleId id, con
return false;
RootedValue tmp(cx, value);
return PutProperty(cx, obj, id, tmp, strict);
ObjectOpResult result;
return SetProperty(cx, obj, id, tmp, receiver, result) &&
result.checkStrictErrorOrWarning(cx, obj, id, strict);
}
static MOZ_NEVER_INLINE bool
@ -1886,18 +1891,10 @@ 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)
@ -2661,6 +2658,23 @@ CASE(JSOP_CALLPROP)
}
END_CASE(JSOP_GETPROP)
CASE(JSOP_GETPROP_SUPER)
{
RootedObject& receiver = rootObject0;
RootedObject& obj = rootObject1;
FETCH_OBJECT(cx, -2, receiver);
obj = &REGS.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;
@ -2730,6 +2744,38 @@ 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 = &REGS.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)
{
@ -2751,6 +2797,27 @@ 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 = &REGS.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)
{
@ -2761,13 +2828,37 @@ CASE(JSOP_STRICTSETELEM)
RootedId& id = rootId0;
FETCH_ELEMENT_ID(-2, id);
Value& value = REGS.sp[-1];
if (!SetObjectElementOperation(cx, obj, id, value, *REGS.pc == JSOP_STRICTSETELEM))
RootedValue& receiver = rootValue0;
receiver = ObjectValue(*obj);
if (!SetObjectElementOperation(cx, obj, receiver, 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 = &REGS.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)
{
@ -3818,8 +3909,8 @@ CASE(JSOP_CLASSHERITAGE)
}
}
REGS.sp[-1] = objProto;
PUSH_OBJECT(*funcProto);
REGS.sp[-1].setObject(*funcProto);
PUSH_COPY(objProto);
}
END_CASE(JSOP_CLASSHERITAGE)
@ -3854,6 +3945,55 @@ 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 = &REGS.sp[-1].toObject().as<JSFunction>();
MOZ_ASSERT(func->isMethod());
/* Load the home object */
RootedNativeObject& obj = rootNativeObject0;
obj = &REGS.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];
@ -4229,7 +4369,8 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
RootedId id(cx);
if (!ValueToId<CanGC>(cx, index, &id))
return false;
return SetObjectElementOperation(cx, obj, id, value, strict);
RootedValue receiver(cx, ObjectValue(*obj));
return SetObjectElementOperation(cx, obj, receiver, id, value, strict);
}
bool
@ -4240,7 +4381,8 @@ js::SetObjectElement(JSContext* cx, HandleObject obj, HandleValue index, HandleV
RootedId id(cx);
if (!ValueToId<CanGC>(cx, index, &id))
return false;
return SetObjectElementOperation(cx, obj, id, value, strict, script, pc);
RootedValue receiver(cx, ObjectValue(*obj));
return SetObjectElementOperation(cx, obj, receiver, 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 => objProto, funcProto
* Stack: heritage => funcProto, objProto
*/ \
macro(JSOP_CLASSHERITAGE, 51, "classheritage", NULL, 1, 1, 2, JOF_BYTE) \
/*
@ -862,7 +862,13 @@
*/ \
macro(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, JOF_OBJECT) \
\
macro(JSOP_UNUSED92, 92, "unused92", NULL, 1, 0, 0, JOF_BYTE) \
/*
* 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) \
\
/*
* Initialize a named property in an object literal, like '{a: x}'.
@ -982,9 +988,26 @@
*/ \
macro(JSOP_NEWARRAY_COPYONWRITE, 102, "newarray_copyonwrite", NULL, 5, 0, 1, JOF_OBJECT) \
\
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) \
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) \
\
/*
* This opcode precedes every labeled statement. It's a no-op.
@ -998,7 +1021,15 @@
*/ \
macro(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, JOF_JUMP) \
\
macro(JSOP_UNUSED107, 107,"unused107", NULL, 1, 0, 0, JOF_BYTE) \
/*
* 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) \
\
/*
* Invokes 'callee' with 'this' and 'args', pushes return value onto the
@ -1219,7 +1250,15 @@
* nuses: (argc+2)
*/ \
macro(JSOP_STRICTEVAL, 124, "strict-eval", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSTRICT) \
macro(JSOP_UNUSED125, 125, "unused125", NULL, 1, 0, 0, JOF_BYTE) \
/*
* 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_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \
\
/*
@ -1549,8 +1588,24 @@
* Stack: => this
*/ \
macro(JSOP_GIMPLICITTHIS, 157, "gimplicitthis", "", 5, 0, 1, JOF_ATOM) \
macro(JSOP_UNUSED158, 158, "unused158", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED159, 159, "unused159", NULL, 1, 0, 0, JOF_BYTE) \
/*
* 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) \
\
/*
* 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 = 274;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 275;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 392,
static_assert(JSErr_Limit == 395,
"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 "