1339395 - Implement Object Rest/Spread Properties proposal.
parent
dbc2e38812
commit
32bc386753
|
@ -3222,6 +3222,8 @@ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst)
|
|||
return expression(pn->pn_kid, &val) &&
|
||||
builder.prototypeMutation(val, &pn->pn_pos, dst);
|
||||
}
|
||||
if (pn->isKind(PNK_SPREAD))
|
||||
return expression(pn, dst);
|
||||
|
||||
PropKind kind;
|
||||
switch (pn->getOp()) {
|
||||
|
@ -3342,6 +3344,16 @@ ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst)
|
|||
return false;
|
||||
|
||||
for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
|
||||
if (propdef->isKind(PNK_SPREAD)) {
|
||||
RootedValue target(cx);
|
||||
RootedValue spread(cx);
|
||||
if (!pattern(propdef->pn_kid, &target))
|
||||
return false;
|
||||
if(!builder.spreadExpression(target, &propdef->pn_pos, &spread))
|
||||
return false;
|
||||
elts.infallibleAppend(spread);
|
||||
continue;
|
||||
}
|
||||
LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP));
|
||||
|
||||
RootedValue key(cx);
|
||||
|
|
|
@ -229,6 +229,69 @@ function GetInternalError(msg) {
|
|||
// To be used when a function is required but calling it shouldn't do anything.
|
||||
function NullFunction() {}
|
||||
|
||||
// Object Rest/Spread Properties proposal
|
||||
// Abstract operation: CopyDataProperties (target, source, excluded)
|
||||
function CopyDataProperties(target, source, excluded) {
|
||||
// Step 1.
|
||||
assert(IsObject(target), "target is an object");
|
||||
|
||||
// Step 2.
|
||||
assert(IsObject(excluded), "excluded is an object");
|
||||
|
||||
// Steps 3, 6.
|
||||
if (source === undefined || source === null)
|
||||
return;
|
||||
|
||||
// Step 4.a.
|
||||
source = ToObject(source);
|
||||
|
||||
// Step 4.b.
|
||||
var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
||||
|
||||
// Step 5.
|
||||
for (var index = 0; index < keys.length; index++) {
|
||||
var key = keys[index];
|
||||
|
||||
// We abbreviate this by calling propertyIsEnumerable which is faster
|
||||
// and returns false for not defined properties.
|
||||
if (!callFunction(std_Object_hasOwnProperty, key, excluded) && callFunction(std_Object_propertyIsEnumerable, source, key))
|
||||
_DefineDataProperty(target, key, source[key]);
|
||||
}
|
||||
|
||||
// Step 6 (Return).
|
||||
}
|
||||
|
||||
// Object Rest/Spread Properties proposal
|
||||
// Abstract operation: CopyDataProperties (target, source, excluded)
|
||||
function CopyDataPropertiesUnfiltered(target, source) {
|
||||
// Step 1.
|
||||
assert(IsObject(target), "target is an object");
|
||||
|
||||
// Step 2 (Not applicable).
|
||||
|
||||
// Steps 3, 6.
|
||||
if (source === undefined || source === null)
|
||||
return;
|
||||
|
||||
// Step 4.a.
|
||||
source = ToObject(source);
|
||||
|
||||
// Step 4.b.
|
||||
var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
|
||||
|
||||
// Step 5.
|
||||
for (var index = 0; index < keys.length; index++) {
|
||||
var key = keys[index];
|
||||
|
||||
// We abbreviate this by calling propertyIsEnumerable which is faster
|
||||
// and returns false for not defined properties.
|
||||
if (callFunction(std_Object_propertyIsEnumerable, source, key))
|
||||
_DefineDataProperty(target, key, source[key]);
|
||||
}
|
||||
|
||||
// Step 6 (Return).
|
||||
}
|
||||
|
||||
/*************************************** Testing functions ***************************************/
|
||||
function outer() {
|
||||
return function inner() {
|
||||
|
|
|
@ -5814,36 +5814,78 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
|
|||
if (!emitRequireObjectCoercible()) // ... RHS
|
||||
return false;
|
||||
|
||||
bool needsRestPropertyExcludedSet = pattern->pn_count > 1 &&
|
||||
pattern->last()->isKind(PNK_SPREAD);
|
||||
if (needsRestPropertyExcludedSet) {
|
||||
if (!emitDestructuringObjRestExclusionSet(pattern)) // ... RHS SET
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_SWAP)) // ... SET RHS
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
|
||||
ParseNode* subpattern;
|
||||
if (member->isKind(PNK_MUTATEPROTO))
|
||||
if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD))
|
||||
subpattern = member->pn_kid;
|
||||
else
|
||||
subpattern = member->pn_right;
|
||||
|
||||
ParseNode* lhs = subpattern;
|
||||
MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN));
|
||||
if (lhs->isKind(PNK_ASSIGN))
|
||||
lhs = lhs->pn_left;
|
||||
|
||||
size_t emitted;
|
||||
if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF
|
||||
if (!emitDestructuringLHSRef(lhs, &emitted)) // ... *SET RHS *LREF
|
||||
return false;
|
||||
|
||||
// Duplicate the value being destructured to use as a reference base.
|
||||
if (emitted) {
|
||||
if (!emitDupAt(emitted)) // ... RHS *LREF RHS
|
||||
if (!emitDupAt(emitted)) // ... *SET RHS *LREF RHS
|
||||
return false;
|
||||
} else {
|
||||
if (!emit1(JSOP_DUP)) // ... RHS RHS
|
||||
if (!emit1(JSOP_DUP)) // ... *SET RHS RHS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (member->isKind(PNK_SPREAD)) {
|
||||
if (!updateSourceCoordNotes(member->pn_pos.begin))
|
||||
return false;
|
||||
|
||||
if (!emitNewInit(JSProto_Object)) // ... *SET RHS *LREF RHS TARGET
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ... *SET RHS *LREF RHS TARGET TARGET
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, 2)) // ... *SET RHS *LREF TARGET TARGET RHS
|
||||
return false;
|
||||
|
||||
if (needsRestPropertyExcludedSet) {
|
||||
if (!emit2(JSOP_PICK, emitted + 4)) // ... RHS *LREF TARGET TARGET RHS SET
|
||||
return false;
|
||||
}
|
||||
|
||||
CopyOption option = needsRestPropertyExcludedSet
|
||||
? CopyOption::Filtered
|
||||
: CopyOption::Unfiltered;
|
||||
if (!emitCopyDataProperties(option)) // ... RHS *LREF TARGET
|
||||
return false;
|
||||
|
||||
// Destructure TARGET per this member's lhs.
|
||||
if (!emitSetOrInitializeDestructuring(lhs, flav)) // ... RHS
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(member == pattern->last(), "Rest property is always last");
|
||||
break;
|
||||
}
|
||||
|
||||
// Now push the property name currently being matched, which is the
|
||||
// current property name "label" on the left of a colon in the object
|
||||
// initialiser.
|
||||
bool needsGetElem = true;
|
||||
|
||||
if (member->isKind(PNK_MUTATEPROTO)) {
|
||||
if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP
|
||||
if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... *SET RHS *LREF PROP
|
||||
return false;
|
||||
needsGetElem = false;
|
||||
} else {
|
||||
|
@ -5851,40 +5893,131 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla
|
|||
|
||||
ParseNode* key = member->pn_left;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY
|
||||
if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY
|
||||
return false;
|
||||
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
PropertyName* name = key->pn_atom->asPropertyName();
|
||||
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simplification of downstream analysis.
|
||||
jsid id = NameToId(name);
|
||||
if (id != IdToTypeId(id)) {
|
||||
if (!emitTree(key)) // ... RHS *LREF RHS KEY
|
||||
return false;
|
||||
} else {
|
||||
if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP
|
||||
return false;
|
||||
needsGetElem = false;
|
||||
}
|
||||
} else {
|
||||
if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY
|
||||
if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) // ... *SET RHS *LREF PROP
|
||||
return false;
|
||||
needsGetElem = false;
|
||||
} else {
|
||||
if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY
|
||||
return false;
|
||||
|
||||
// Add the computed property key to the exclusion set.
|
||||
if (needsRestPropertyExcludedSet) {
|
||||
if (!emitDupAt(emitted + 3)) // ... SET RHS *LREF RHS KEY SET
|
||||
return false;
|
||||
if (!emitDupAt(1)) // ... SET RHS *LREF RHS KEY SET KEY
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED
|
||||
return false;
|
||||
if (!emit1(JSOP_INITELEM)) // ... SET RHS *LREF RHS KEY SET
|
||||
return false;
|
||||
if (!emit1(JSOP_POP)) // ... SET RHS *LREF RHS KEY
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the property value if not done already.
|
||||
if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP
|
||||
if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... *SET RHS *LREF PROP
|
||||
return false;
|
||||
|
||||
if (subpattern->isKind(PNK_ASSIGN)) {
|
||||
if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE
|
||||
if (!emitDefault(subpattern->pn_right, lhs)) // ... *SET RHS *LREF VALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destructure PROP per this member's lhs.
|
||||
if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS
|
||||
if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... *SET RHS
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern)
|
||||
{
|
||||
MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
|
||||
MOZ_ASSERT(pattern->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD));
|
||||
|
||||
ptrdiff_t offset = this->offset();
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
return false;
|
||||
|
||||
// Try to construct the shape of the object as we go, so we can emit a
|
||||
// JSOP_NEWOBJECT with the final shape instead.
|
||||
// In the case of computed property names and indices, we cannot fix the
|
||||
// shape at bytecode compile time. When the shape cannot be determined,
|
||||
// |obj| is nulled out.
|
||||
|
||||
// No need to do any guessing for the object kind, since we know the upper
|
||||
// bound of how many properties we plan to have.
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1);
|
||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
RootedAtom pnatom(cx);
|
||||
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
|
||||
if (member->isKind(PNK_SPREAD))
|
||||
break;
|
||||
|
||||
bool isIndex = false;
|
||||
if (member->isKind(PNK_MUTATEPROTO)) {
|
||||
pnatom.set(cx->names().proto);
|
||||
} else {
|
||||
ParseNode* key = member->pn_left;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!emitNumberOp(key->pn_dval))
|
||||
return false;
|
||||
isIndex = true;
|
||||
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
pnatom.set(key->pn_atom);
|
||||
} else {
|
||||
// Otherwise this is a computed property name which needs to
|
||||
// be added dynamically.
|
||||
obj.set(nullptr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize elements with |undefined|.
|
||||
if (!emit1(JSOP_UNDEFINED))
|
||||
return false;
|
||||
|
||||
if (isIndex) {
|
||||
obj.set(nullptr);
|
||||
if (!emit1(JSOP_INITELEM))
|
||||
return false;
|
||||
} else {
|
||||
uint32_t index;
|
||||
if (!makeAtomIndex(pnatom, &index))
|
||||
return false;
|
||||
|
||||
if (obj) {
|
||||
MOZ_ASSERT(!obj->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(pnatom));
|
||||
if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (obj->inDictionaryMode())
|
||||
obj.set(nullptr);
|
||||
}
|
||||
|
||||
if (!emitIndex32(JSOP_INITPROP, index))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
// The object survived and has a predictable shape: update the
|
||||
// original bytecode.
|
||||
if (!replaceNewInitWithNewObject(obj, offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6765,6 +6898,53 @@ BytecodeEmitter::emitRequireObjectCoercible()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitCopyDataProperties(CopyOption option)
|
||||
{
|
||||
DebugOnly<int32_t> depth = this->stackDepth;
|
||||
|
||||
uint32_t argc;
|
||||
if (option == CopyOption::Filtered) {
|
||||
MOZ_ASSERT(depth > 2); // TARGET SOURCE SET
|
||||
argc = 3;
|
||||
|
||||
if (!emitAtomOp(cx->names().CopyDataProperties,
|
||||
JSOP_GETINTRINSIC)) // TARGET SOURCE SET COPYDATAPROPERTIES
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(depth > 1); // TARGET SOURCE
|
||||
argc = 2;
|
||||
|
||||
if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered,
|
||||
JSOP_GETINTRINSIC)) // TARGET SOURCE COPYDATAPROPERTIES
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_UNDEFINED)) // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, argc + 1)) // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET
|
||||
return false;
|
||||
if (!emit2(JSOP_PICK, argc + 1)) // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE
|
||||
return false;
|
||||
if (option == CopyOption::Filtered) {
|
||||
if (!emit2(JSOP_PICK, argc + 1)) // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET
|
||||
return false;
|
||||
}
|
||||
if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED
|
||||
return false;
|
||||
checkTypeSet(JSOP_CALL_IGNORES_RV);
|
||||
|
||||
if (!emit1(JSOP_POP)) // -
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(depth - int(argc) == this->stackDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitIterator()
|
||||
{
|
||||
|
@ -9449,6 +9629,22 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (propdef->isKind(PNK_SPREAD)) {
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
|
||||
if (!emit1(JSOP_DUP))
|
||||
return false;
|
||||
|
||||
if (!emitTree(propdef->pn_kid))
|
||||
return false;
|
||||
|
||||
if (!emitCopyDataProperties(CopyOption::Unfiltered))
|
||||
return false;
|
||||
|
||||
objp.set(nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool extraPop = false;
|
||||
if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
|
||||
extraPop = true;
|
||||
|
@ -9472,16 +9668,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simpliciation of downstream analysis.
|
||||
jsid id = NameToId(key->pn_atom->asPropertyName());
|
||||
if (id != IdToTypeId(id)) {
|
||||
if (!emitTree(key))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
} else {
|
||||
if (!emitComputedPropertyName(key))
|
||||
return false;
|
||||
|
@ -9562,8 +9748,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
|||
MOZ_ASSERT(!IsHiddenInitOp(op));
|
||||
MOZ_ASSERT(!objp->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr,
|
||||
if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
|
@ -9606,15 +9791,16 @@ BytecodeEmitter::emitObject(ParseNode* pn)
|
|||
if (!emitNewInit(JSProto_Object))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Try to construct the shape of the object as we go, so we can emit a
|
||||
* JSOP_NEWOBJECT with the final shape instead.
|
||||
*/
|
||||
RootedPlainObject obj(cx);
|
||||
// No need to do any guessing for the object kind, since we know exactly
|
||||
// how many properties we plan to have.
|
||||
// Try to construct the shape of the object as we go, so we can emit a
|
||||
// JSOP_NEWOBJECT with the final shape instead.
|
||||
// In the case of computed property names and indices, we cannot fix the
|
||||
// shape at bytecode compile time. When the shape cannot be determined,
|
||||
// |obj| is nulled out.
|
||||
|
||||
// No need to do any guessing for the object kind, since we know the upper
|
||||
// bound of how many properties we plan to have.
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count);
|
||||
obj = NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject);
|
||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
|
@ -9622,29 +9808,38 @@ BytecodeEmitter::emitObject(ParseNode* pn)
|
|||
return false;
|
||||
|
||||
if (obj) {
|
||||
/*
|
||||
* The object survived and has a predictable shape: update the original
|
||||
* bytecode.
|
||||
*/
|
||||
ObjectBox* objbox = parser->newObjectBox(obj);
|
||||
if (!objbox)
|
||||
// The object survived and has a predictable shape: update the original
|
||||
// bytecode.
|
||||
if (!replaceNewInitWithNewObject(obj, offset))
|
||||
return false;
|
||||
|
||||
static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
|
||||
"newinit and newobject must have equal length to edit in-place");
|
||||
|
||||
uint32_t index = objectList.add(objbox);
|
||||
jsbytecode* code = this->code(offset);
|
||||
code[0] = JSOP_NEWOBJECT;
|
||||
code[1] = jsbytecode(index >> 24);
|
||||
code[2] = jsbytecode(index >> 16);
|
||||
code[3] = jsbytecode(index >> 8);
|
||||
code[4] = jsbytecode(index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
|
||||
{
|
||||
ObjectBox* objbox = parser->newObjectBox(obj);
|
||||
if (!objbox)
|
||||
return false;
|
||||
|
||||
static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
|
||||
"newinit and newobject must have equal length to edit in-place");
|
||||
|
||||
uint32_t index = objectList.add(objbox);
|
||||
jsbytecode* code = this->code(offset);
|
||||
|
||||
MOZ_ASSERT(code[0] == JSOP_NEWINIT);
|
||||
code[0] = JSOP_NEWOBJECT;
|
||||
code[1] = jsbytecode(index >> 24);
|
||||
code[2] = jsbytecode(index >> 16);
|
||||
code[3] = jsbytecode(index >> 8);
|
||||
code[4] = jsbytecode(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitArrayComp(ParseNode* pn)
|
||||
{
|
||||
|
|
|
@ -540,6 +540,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false);
|
||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
|
||||
|
||||
MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
||||
|
@ -667,6 +669,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
// []/{} expression).
|
||||
MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav);
|
||||
|
||||
// emitDestructuringObjRestExclusionSet emits the property exclusion set
|
||||
// for the rest-property in an object pattern.
|
||||
MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern);
|
||||
|
||||
// emitDestructuringOps assumes the to-be-destructured value has been
|
||||
// pushed on the stack and emits code to destructure each part of a [] or
|
||||
// {} lhs expression.
|
||||
|
@ -684,6 +690,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
// object, with no overall effect on the stack.
|
||||
MOZ_MUST_USE bool emitRequireObjectCoercible();
|
||||
|
||||
enum class CopyOption {
|
||||
Filtered, Unfiltered
|
||||
};
|
||||
|
||||
// Calls either the |CopyDataProperties| or the
|
||||
// |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or
|
||||
// two in the latter case) elements from the stack.
|
||||
MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option);
|
||||
|
||||
// emitIterator expects the iterable to already be on the stack.
|
||||
// It will replace that stack value with the corresponding iterator
|
||||
MOZ_MUST_USE bool emitIterator();
|
||||
|
|
|
@ -1345,15 +1345,8 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>&
|
|||
if (!name)
|
||||
return true;
|
||||
|
||||
// Also don't optimize if the name doesn't map directly to its id for TI's
|
||||
// purposes.
|
||||
if (NameToId(name) != IdToTypeId(NameToId(name)))
|
||||
return true;
|
||||
|
||||
// Optimization 3: We have expr["foo"] where foo is not an index. Convert
|
||||
// to a property access (like expr.foo) that optimizes better downstream.
|
||||
// Don't bother with this for names that TI considers to be indexes, to
|
||||
// simplify downstream analysis.
|
||||
ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
|
||||
if (!dottedAccess)
|
||||
return false;
|
||||
|
|
|
@ -302,10 +302,9 @@ class FullParseHandler
|
|||
}
|
||||
|
||||
MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) {
|
||||
TokenPos pos(begin, inner->pn_pos.end);
|
||||
ParseNode* spread = new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, inner);
|
||||
ParseNode* spread = newSpread(begin, inner);
|
||||
if (!spread)
|
||||
return null();
|
||||
return false;
|
||||
literal->append(spread);
|
||||
literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
|
||||
return true;
|
||||
|
@ -396,6 +395,18 @@ class FullParseHandler
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) {
|
||||
MOZ_ASSERT(literal->isKind(PNK_OBJECT));
|
||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||
|
||||
setListFlag(literal, PNX_NONCONST);
|
||||
ParseNode* spread = newSpread(begin, inner);
|
||||
if (!spread)
|
||||
return false;
|
||||
literal->append(spread);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
|
||||
JSOp op)
|
||||
{
|
||||
|
|
|
@ -4361,85 +4361,105 @@ Parser<ParseHandler>::objectBindingPattern(DeclarationKind kind, YieldHandling y
|
|||
RootedAtom propAtom(context);
|
||||
for (;;) {
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
break;
|
||||
|
||||
TokenPos namePos = pos();
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
// rest-binding property
|
||||
tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
tokenStream.ungetToken();
|
||||
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
|
||||
if (!propName)
|
||||
return null();
|
||||
|
||||
if (propType == PropertyType::Normal) {
|
||||
// Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
|
||||
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
||||
Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
|
||||
if (!binding)
|
||||
Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
|
||||
if (!inner)
|
||||
return null();
|
||||
|
||||
bool hasInitializer;
|
||||
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
|
||||
return null();
|
||||
|
||||
Node bindingExpr = hasInitializer
|
||||
? bindingInitializer(binding, kind, yieldHandling)
|
||||
: binding;
|
||||
if (!bindingExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
|
||||
return null();
|
||||
} else if (propType == PropertyType::Shorthand) {
|
||||
// Handle e.g., |var {x, y} = o| as destructuring shorthand
|
||||
// for |var {x: x, y: y} = o|.
|
||||
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
|
||||
|
||||
Node binding = bindingIdentifier(kind, yieldHandling);
|
||||
if (!binding)
|
||||
return null();
|
||||
|
||||
if (!handler.addShorthand(literal, propName, binding))
|
||||
return null();
|
||||
} else if (propType == PropertyType::CoverInitializedName) {
|
||||
// Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand
|
||||
// with default values.
|
||||
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
|
||||
|
||||
Node binding = bindingIdentifier(kind, yieldHandling);
|
||||
if (!binding)
|
||||
return null();
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_ASSIGN);
|
||||
|
||||
Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
|
||||
if (!bindingExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
|
||||
if (!handler.addSpreadProperty(literal, begin, inner))
|
||||
return null();
|
||||
} else {
|
||||
errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
TokenPos namePos = tokenStream.nextToken().pos;
|
||||
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
|
||||
if (!propName)
|
||||
return null();
|
||||
if (propType == PropertyType::Normal) {
|
||||
// Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
|
||||
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
|
||||
if (!binding)
|
||||
return null();
|
||||
|
||||
bool hasInitializer;
|
||||
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
|
||||
return null();
|
||||
|
||||
Node bindingExpr = hasInitializer
|
||||
? bindingInitializer(binding, kind, yieldHandling)
|
||||
: binding;
|
||||
if (!bindingExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
|
||||
return null();
|
||||
} else if (propType == PropertyType::Shorthand) {
|
||||
// Handle e.g., |var {x, y} = o| as destructuring shorthand
|
||||
// for |var {x: x, y: y} = o|.
|
||||
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
|
||||
|
||||
Node binding = bindingIdentifier(kind, yieldHandling);
|
||||
if (!binding)
|
||||
return null();
|
||||
|
||||
if (!handler.addShorthand(literal, propName, binding))
|
||||
return null();
|
||||
} else if (propType == PropertyType::CoverInitializedName) {
|
||||
// Handle e.g., |var {x=1, y=2} = o| as destructuring
|
||||
// shorthand with default values.
|
||||
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
|
||||
|
||||
Node binding = bindingIdentifier(kind, yieldHandling);
|
||||
if (!binding)
|
||||
return null();
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_ASSIGN);
|
||||
|
||||
Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
|
||||
if (!bindingExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
|
||||
return null();
|
||||
} else {
|
||||
errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokenStream.getToken(&tt))
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
if (!matched)
|
||||
break;
|
||||
if (tt != TOK_COMMA) {
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin);
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
error(JSMSG_REST_WITH_COMMA);
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, begin));
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
}
|
||||
|
@ -9682,173 +9702,200 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
|
|||
RootedAtom propAtom(context);
|
||||
for (;;) {
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
break;
|
||||
|
||||
TokenPos namePos = pos();
|
||||
|
||||
tokenStream.ungetToken();
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
// object spread
|
||||
tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
|
||||
if (!propName)
|
||||
return null();
|
||||
|
||||
if (propType == PropertyType::Normal) {
|
||||
TokenPos exprPos;
|
||||
if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
|
||||
return null();
|
||||
Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
|
||||
possibleError);
|
||||
if (!propExpr)
|
||||
TokenPos innerPos;
|
||||
if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
|
||||
Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
|
||||
possibleError);
|
||||
if (!inner)
|
||||
return null();
|
||||
if (possibleError)
|
||||
checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
|
||||
if (!handler.addSpreadProperty(literal, begin, inner))
|
||||
return null();
|
||||
} else {
|
||||
TokenPos namePos = tokenStream.nextToken().pos;
|
||||
|
||||
PropertyType propType;
|
||||
Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
|
||||
if (!propName)
|
||||
|
||||
if (foldConstants && !FoldConstants(context, &propExpr, this))
|
||||
return null();
|
||||
|
||||
if (propAtom == context->names().proto) {
|
||||
if (seenPrototypeMutation) {
|
||||
// Directly report the error when we're not in a
|
||||
// destructuring context.
|
||||
if (propType == PropertyType::Normal) {
|
||||
TokenPos exprPos;
|
||||
if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
|
||||
possibleError);
|
||||
if (!propExpr)
|
||||
return null();
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
|
||||
|
||||
if (possibleError)
|
||||
checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
|
||||
|
||||
if (foldConstants && !FoldConstants(context, &propExpr, this))
|
||||
return null();
|
||||
|
||||
if (propAtom == context->names().proto) {
|
||||
if (seenPrototypeMutation) {
|
||||
// Directly report the error when we're not in a
|
||||
// destructuring context.
|
||||
if (!possibleError) {
|
||||
errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Otherwise delay error reporting until we've
|
||||
// determined whether or not we're destructuring.
|
||||
possibleError->setPendingExpressionErrorAt(namePos,
|
||||
JSMSG_DUPLICATE_PROTO_PROPERTY);
|
||||
}
|
||||
seenPrototypeMutation = true;
|
||||
|
||||
// Note: this occurs *only* if we observe TOK_COLON! Only
|
||||
// __proto__: v mutates [[Prototype]]. Getters, setters,
|
||||
// method/generator definitions, computed property name
|
||||
// versions of all of these, and shorthands do not.
|
||||
if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
|
||||
return null();
|
||||
} else {
|
||||
if (!handler.isConstant(propExpr))
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, propExpr))
|
||||
return null();
|
||||
}
|
||||
} else if (propType == PropertyType::Shorthand) {
|
||||
/*
|
||||
* Support, e.g., |({x, y} = o)| as destructuring shorthand
|
||||
* for |({x: x, y: y} = o)|, and |var o = {x, y}| as
|
||||
* initializer shorthand for |var o = {x: x, y: y}|.
|
||||
*/
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node nameExpr = identifierReference(name);
|
||||
if (!nameExpr)
|
||||
return null();
|
||||
|
||||
if (possibleError)
|
||||
checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
|
||||
|
||||
if (!handler.addShorthand(literal, propName, nameExpr))
|
||||
return null();
|
||||
} else if (propType == PropertyType::CoverInitializedName) {
|
||||
/*
|
||||
* Support, e.g., |({x=1, y=2} = o)| as destructuring
|
||||
* shorthand with default values, as per ES6 12.14.5
|
||||
*/
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node lhs = identifierReference(name);
|
||||
if (!lhs)
|
||||
return null();
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_ASSIGN);
|
||||
|
||||
if (!seenCoverInitializedName) {
|
||||
// "shorthand default" or "CoverInitializedName" syntax is
|
||||
// only valid in the case of destructuring.
|
||||
seenCoverInitializedName = true;
|
||||
|
||||
if (!possibleError) {
|
||||
errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
|
||||
// Destructuring defaults are definitely not allowed
|
||||
// in this object literal, because of something the
|
||||
// caller knows about the preceding code. For example,
|
||||
// maybe the preceding token is an operator:
|
||||
// |x + {y=z}|.
|
||||
error(JSMSG_COLON_AFTER_ID);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Otherwise delay error reporting until we've determined
|
||||
// whether or not we're destructuring.
|
||||
possibleError->setPendingExpressionErrorAt(namePos,
|
||||
JSMSG_DUPLICATE_PROTO_PROPERTY);
|
||||
// Here we set a pending error so that later in the parse,
|
||||
// once we've determined whether or not we're
|
||||
// destructuring, the error can be reported or ignored
|
||||
// appropriately.
|
||||
possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
|
||||
}
|
||||
seenPrototypeMutation = true;
|
||||
|
||||
// Note: this occurs *only* if we observe TOK_COLON! Only
|
||||
// __proto__: v mutates [[Prototype]]. Getters, setters,
|
||||
// method/generator definitions, computed property name
|
||||
// versions of all of these, and shorthands do not.
|
||||
if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
|
||||
if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
|
||||
// |chars| is "arguments" or "eval" here.
|
||||
if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
|
||||
return null();
|
||||
}
|
||||
|
||||
Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!rhs)
|
||||
return null();
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(rhs);
|
||||
|
||||
Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
|
||||
if (!propExpr)
|
||||
return null();
|
||||
} else {
|
||||
if (!handler.isConstant(propExpr))
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, propExpr))
|
||||
return null();
|
||||
}
|
||||
} else {
|
||||
RootedAtom funName(context);
|
||||
if (!tokenStream.isCurrentTokenType(TOK_RB)) {
|
||||
funName = propAtom;
|
||||
|
||||
if (possibleError)
|
||||
checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
|
||||
} else if (propType == PropertyType::Shorthand) {
|
||||
/*
|
||||
* Support, e.g., |({x, y} = o)| as destructuring shorthand
|
||||
* for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer
|
||||
* shorthand for |var o = {x: x, y: y}|.
|
||||
*/
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node nameExpr = identifierReference(name);
|
||||
if (!nameExpr)
|
||||
return null();
|
||||
|
||||
if (possibleError)
|
||||
checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
|
||||
|
||||
if (!handler.addShorthand(literal, propName, nameExpr))
|
||||
return null();
|
||||
} else if (propType == PropertyType::CoverInitializedName) {
|
||||
/*
|
||||
* Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand
|
||||
* with default values, as per ES6 12.14.5
|
||||
*/
|
||||
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
|
||||
if (!name)
|
||||
return null();
|
||||
|
||||
Node lhs = identifierReference(name);
|
||||
if (!lhs)
|
||||
return null();
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_ASSIGN);
|
||||
|
||||
if (!seenCoverInitializedName) {
|
||||
// "shorthand default" or "CoverInitializedName" syntax is only
|
||||
// valid in the case of destructuring.
|
||||
seenCoverInitializedName = true;
|
||||
|
||||
if (!possibleError) {
|
||||
// Destructuring defaults are definitely not allowed in this object literal,
|
||||
// because of something the caller knows about the preceding code.
|
||||
// For example, maybe the preceding token is an operator: `x + {y=z}`.
|
||||
error(JSMSG_COLON_AFTER_ID);
|
||||
return null();
|
||||
if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
|
||||
funName = prefixAccessorName(propType, propAtom);
|
||||
if (!funName)
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
// Here we set a pending error so that later in the parse, once we've
|
||||
// determined whether or not we're destructuring, the error can be
|
||||
// reported or ignored appropriately.
|
||||
possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
|
||||
}
|
||||
|
||||
if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
|
||||
// |chars| is "arguments" or "eval" here.
|
||||
if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
|
||||
Node fn = methodDefinition(namePos.begin, propType, funName);
|
||||
if (!fn)
|
||||
return null();
|
||||
}
|
||||
|
||||
Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!rhs)
|
||||
return null();
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(fn);
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(rhs);
|
||||
JSOp op = JSOpFromPropertyType(propType);
|
||||
if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
|
||||
return null();
|
||||
|
||||
Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
|
||||
if (!propExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propName, propExpr))
|
||||
return null();
|
||||
} else {
|
||||
RootedAtom funName(context);
|
||||
if (!tokenStream.isCurrentTokenType(TOK_RB)) {
|
||||
funName = propAtom;
|
||||
|
||||
if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
|
||||
funName = prefixAccessorName(propType, propAtom);
|
||||
if (!funName)
|
||||
return null();
|
||||
if (possibleError) {
|
||||
possibleError->setPendingDestructuringErrorAt(namePos,
|
||||
JSMSG_BAD_DESTRUCT_TARGET);
|
||||
}
|
||||
}
|
||||
|
||||
Node fn = methodDefinition(namePos.begin, propType, funName);
|
||||
if (!fn)
|
||||
return null();
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(fn);
|
||||
|
||||
JSOp op = JSOpFromPropertyType(propType);
|
||||
if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
|
||||
return null();
|
||||
|
||||
if (possibleError)
|
||||
possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET);
|
||||
}
|
||||
|
||||
if (!tokenStream.getToken(&tt))
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
if (!matched)
|
||||
break;
|
||||
if (tt != TOK_COMMA) {
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos);
|
||||
return null();
|
||||
}
|
||||
if (tt == TOK_TRIPLEDOT && possibleError)
|
||||
possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
|
||||
reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
|
||||
JSMSG_CURLY_OPENED, openedPos));
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
}
|
||||
|
|
|
@ -298,6 +298,7 @@ class SyntaxParseHandler
|
|||
MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; }
|
||||
MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; }
|
||||
MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
|
||||
MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
|
||||
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
// Tests that wasm module scripts have special URLs.
|
||||
|
||||
if (!wasmIsSupported())
|
||||
quit();
|
||||
|
||||
var g = newGlobal();
|
||||
g.eval(`
|
||||
function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
|
||||
o = initWasm('(module (func) (export "" 0))');
|
||||
o2 = initWasm('(module (func) (func) (export "" 1))');
|
||||
`);
|
||||
|
||||
function isWasm(script) { return script.format === "wasm"; }
|
||||
|
||||
function isValidWasmURL(url) {
|
||||
// The URLs will have the following format:
|
||||
// wasm: [<uri-econded-filename-of-host> ":"] <64-bit-hash>
|
||||
return /^wasm:(?:[^:]*:)*?[0-9a-f]{16}$/.test(url);
|
||||
}
|
||||
|
||||
var dbg = new Debugger(g);
|
||||
var foundScripts = dbg.findScripts().filter(isWasm);
|
||||
assertEq(foundScripts.length, 2);
|
||||
assertEq(isValidWasmURL(foundScripts[0].source.url), true);
|
||||
assertEq(isValidWasmURL(foundScripts[1].source.url), true);
|
||||
assertEq(foundScripts[0].source.url != foundScripts[1].source.url, true);
|
|
@ -39,7 +39,7 @@ testThrow(`
|
|||
|
||||
testThrow(`
|
||||
({...a)=>
|
||||
`, 2);
|
||||
`, 6);
|
||||
|
||||
testThrow(`
|
||||
function f([... ...a)=>
|
||||
|
@ -47,7 +47,7 @@ function f([... ...a)=>
|
|||
|
||||
testThrow(`
|
||||
function f({...a)=>
|
||||
`, 12);
|
||||
`, 16);
|
||||
|
||||
// arrow
|
||||
|
||||
|
@ -67,7 +67,7 @@ var [... ...a)=>
|
|||
|
||||
testThrow(`
|
||||
var {...a)=>
|
||||
`, 5);
|
||||
`, 9);
|
||||
|
||||
// initializer
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
|
||||
function property(key, value = key, shorthand = key === value) {
|
||||
return { key, value, shorthand };
|
||||
}
|
||||
|
||||
function assertDestrAssign(src, pattern) {
|
||||
assertExpr(`(${src} = 0)`, aExpr("=", pattern, lit(0)));
|
||||
}
|
||||
|
||||
function assertDestrBinding(src, pattern) {
|
||||
assertDecl(`var ${src} = 0`, varDecl([{id: pattern, init: lit(0)}]));
|
||||
}
|
||||
|
||||
function test() {
|
||||
// Target expression must be a simple assignment target or a nested pattern
|
||||
// in object assignment patterns.
|
||||
assertDestrAssign("{...x}", objPatt([spread(ident("x"))]));
|
||||
assertDestrAssign("{...(x)}", objPatt([spread(ident("x"))]));
|
||||
assertDestrAssign("{...obj.p}", objPatt([spread(dotExpr(ident("obj"), ident("p")))]));
|
||||
assertDestrAssign("{...{}}", objPatt([spread(objPatt([]))]));
|
||||
assertDestrAssign("{...[]}", objPatt([spread(arrPatt([]))]));
|
||||
|
||||
// Object binding patterns only allow binding identifiers or nested patterns.
|
||||
assertDestrBinding("{...x}", objPatt([spread(ident("x"))]));
|
||||
assertDestrBinding("{...{}}", objPatt([spread(objPatt([]))]));
|
||||
assertDestrBinding("{...[]}", objPatt([spread(arrPatt([]))]));
|
||||
|
||||
// The rest-property can be preceded by other properties.
|
||||
for (var assertDestr of [assertDestrAssign, assertDestrBinding]) {
|
||||
assertDestr("{a, ...x}", objPatt([property(ident("a")), spread(ident("x"))]));
|
||||
assertDestr("{a: b, ...x}", objPatt([property(ident("a"), ident("b")), spread(ident("x"))]));
|
||||
assertDestr("{[a]: b, ...x}", objPatt([property(comp(ident("a")), ident("b")), spread(ident("x"))]));
|
||||
}
|
||||
|
||||
// Tests when __proto__ is used in the object pattern.
|
||||
for (var assertDestr of [assertDestrAssign, assertDestrBinding]) {
|
||||
assertDestr("{...__proto__}", objPatt([spread(ident("__proto__"))]));
|
||||
assertDestr("{__proto__, ...x}", objPatt([property(ident("__proto__")), spread(ident("x"))]));
|
||||
}
|
||||
assertDestrAssign("{__proto__: a, ...x}", objPatt([property(lit("__proto__"), ident("a")), spread(ident("x"))]));
|
||||
assertDestrBinding("{__proto__: a, ...x}", objPatt([property(ident("__proto__"), ident("a")), spread(ident("x"))]));
|
||||
}
|
||||
|
||||
runtest(test);
|
|
@ -0,0 +1,29 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
|
||||
function property(key, value = key, shorthand = key === value) {
|
||||
return { key, value, shorthand };
|
||||
}
|
||||
|
||||
function test() {
|
||||
// Any expression can be spreaded.
|
||||
assertExpr("({...x})", objExpr([spread(ident("x"))]));
|
||||
assertExpr("({...f()})", objExpr([spread(callExpr(ident("f"), []))]));
|
||||
assertExpr("({...123})", objExpr([spread(lit(123))]));
|
||||
|
||||
// Multiple spread expression are allowed.
|
||||
assertExpr("({...x, ...obj.p})", objExpr([spread(ident("x")), spread(dotExpr(ident("obj"), ident("p")))]));
|
||||
|
||||
// Spread expression can appear anywhere in an object literal.
|
||||
assertExpr("({p, ...x})", objExpr([property(ident("p")), spread(ident("x"))]));
|
||||
assertExpr("({p: a, ...x})", objExpr([property(ident("p"), ident("a")), spread(ident("x"))]));
|
||||
assertExpr("({...x, p: a})", objExpr([spread(ident("x")), property(ident("p"), ident("a"))]));
|
||||
|
||||
// Trailing comma after spread expression is allowed.
|
||||
assertExpr("({...x,})", objExpr([spread(ident("x"))]));
|
||||
|
||||
// __proto__ is not special in spread expressions.
|
||||
assertExpr("({...__proto__})", objExpr([spread(ident("__proto__"))]));
|
||||
assertExpr("({...__proto__, ...__proto__})", objExpr([spread(ident("__proto__")), spread(ident("__proto__"))]));
|
||||
}
|
||||
|
||||
runtest(test);
|
|
@ -71,6 +71,8 @@
|
|||
macro(constructor, constructor, "constructor") \
|
||||
macro(continue, continue_, "continue") \
|
||||
macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
|
||||
macro(CopyDataProperties, CopyDataProperties, "CopyDataProperties") \
|
||||
macro(CopyDataPropertiesUnfiltered, CopyDataPropertiesUnfiltered, "CopyDataPropertiesUnfiltered") \
|
||||
macro(copyWithin, copyWithin, "copyWithin") \
|
||||
macro(count, count, "count") \
|
||||
macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \
|
||||
|
|
Loading…
Reference in New Issue