зеркало из https://github.com/mozilla/gecko-dev.git
Bug 923160 - Disallow initializers in for-of statements. r=jorendorff
This commit is contained in:
Родитель
8005abd5e9
Коммит
a84abcd5e0
|
@ -3236,10 +3236,10 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
/*
|
||||
* Emit variable binding ops, but not destructuring ops. The
|
||||
* parser (see Parser::variables) has ensured that our caller
|
||||
* will be the PNK_FOR/PNK_FORIN case in EmitTree, and that
|
||||
* case will emit the destructuring code only after emitting an
|
||||
* enumerating opcode and a branch that tests whether the
|
||||
* enumeration ended.
|
||||
* will be the PNK_FOR/PNK_FORIN/PNK_FOROF case in EmitTree, and
|
||||
* that case will emit the destructuring code only after
|
||||
* emitting an enumerating opcode and a branch that tests
|
||||
* whether the enumeration ended.
|
||||
*/
|
||||
JS_ASSERT(emitOption == DefineVars);
|
||||
JS_ASSERT(pn->pn_count == 1);
|
||||
|
@ -4745,12 +4745,12 @@ EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff
|
|||
static inline bool
|
||||
EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
{
|
||||
if (pn->pn_left->isKind(PNK_FORIN)) {
|
||||
// FIXME: Give for-of loops their own PNK. Bug 922066.
|
||||
if (pn->pn_iflags == JSITER_FOR_OF)
|
||||
return EmitForOf(cx, bce, pn, top);
|
||||
if (pn->pn_left->isKind(PNK_FORIN))
|
||||
return EmitForIn(cx, bce, pn, top);
|
||||
}
|
||||
|
||||
if (pn->pn_left->isKind(PNK_FOROF))
|
||||
return EmitForOf(cx, bce, pn, top);
|
||||
|
||||
JS_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
|
||||
return EmitNormalFor(cx, bce, pn, top);
|
||||
}
|
||||
|
|
|
@ -332,10 +332,10 @@ class FullParseHandler
|
|||
return pn;
|
||||
}
|
||||
|
||||
ParseNode *newForHead(bool isForInOrOf, ParseNode *pn1, ParseNode *pn2, ParseNode *pn3,
|
||||
ParseNode *newForHead(ParseNodeKind kind, ParseNode *pn1, ParseNode *pn2, ParseNode *pn3,
|
||||
const TokenPos &pos)
|
||||
{
|
||||
ParseNodeKind kind = isForInOrOf ? PNK_FORIN : PNK_FORHEAD;
|
||||
JS_ASSERT(kind == PNK_FORIN || kind == PNK_FOROF || kind == PNK_FORHEAD);
|
||||
return new_<TernaryNode>(kind, JSOP_NOP, pn1, pn2, pn3, pos);
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ class UpvarCookie
|
|||
F(LET) \
|
||||
F(SEQ) \
|
||||
F(FORIN) \
|
||||
F(FOROF) \
|
||||
F(FORHEAD) \
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
|
@ -252,8 +253,8 @@ enum ParseNodeKind
|
|||
* pn_val: constant value if lookup or table switch
|
||||
* PNK_WHILE binary pn_left: cond, pn_right: body
|
||||
* PNK_DOWHILE binary pn_left: body, pn_right: cond
|
||||
* PNK_FOR binary pn_left: either PNK_FORIN (for-in statement) or
|
||||
* PNK_FORHEAD (for(;;) statement)
|
||||
* PNK_FOR binary pn_left: either PNK_FORIN (for-in statement),
|
||||
* PNK_FOROF (for-of) or PNK_FORHEAD (for(;;))
|
||||
* pn_right: body
|
||||
* PNK_FORIN ternary pn_kid1: PNK_VAR to left of 'in', or nullptr
|
||||
* its pn_xflags may have PNX_POPVAR
|
||||
|
@ -262,6 +263,13 @@ enum ParseNodeKind
|
|||
* to left of 'in'; if pn_kid1, then this
|
||||
* is a clone of pn_kid1->pn_head
|
||||
* pn_kid3: object expr to right of 'in'
|
||||
* PNK_FOROF ternary pn_kid1: PNK_VAR to left of 'of', or nullptr
|
||||
* its pn_xflags may have PNX_POPVAR
|
||||
* bit set
|
||||
* pn_kid2: PNK_NAME or destructuring expr
|
||||
* to left of 'of'; if pn_kid1, then this
|
||||
* is a clone of pn_kid1->pn_head
|
||||
* pn_kid3: expr to right of 'of'
|
||||
* PNK_FORHEAD ternary pn_kid1: init expr before first ';' or nullptr
|
||||
* pn_kid2: cond expr before second ';' or nullptr
|
||||
* pn_kid3: update expr after second ';' or nullptr
|
||||
|
|
|
@ -3832,17 +3832,19 @@ Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
|
|||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version,
|
||||
bool isForDecl, bool isForEach, bool isForOf)
|
||||
bool isForDecl, bool isForEach,
|
||||
ParseNodeKind headKind)
|
||||
{
|
||||
if (isForDecl) {
|
||||
if (pn1->pn_count > 1)
|
||||
return false;
|
||||
if (pn1->isOp(JSOP_DEFCONST))
|
||||
return false;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
// In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
|
||||
// Hence all other destructuring decls are banned there.
|
||||
if (version == JSVERSION_1_7 && !isForEach && !isForOf) {
|
||||
if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) {
|
||||
ParseNode *lhs = pn1->pn_head;
|
||||
if (lhs->isKind(PNK_ASSIGN))
|
||||
lhs = lhs->pn_left;
|
||||
|
@ -3868,7 +3870,7 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
|
|||
case PNK_OBJECT:
|
||||
// In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
|
||||
// Hence all other destructuring left-hand sides are banned there.
|
||||
if (version == JSVERSION_1_7 && !isForEach && !isForOf)
|
||||
if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN)
|
||||
return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2;
|
||||
return true;
|
||||
#endif
|
||||
|
@ -3982,9 +3984,14 @@ Parser<FullParseHandler>::forStatement()
|
|||
*/
|
||||
StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
|
||||
ParseNode *pn2, *pn3; /* forHead->pn_kid2 and pn_kid3. */
|
||||
bool isForOf;
|
||||
bool isForInOrOf = pn1 && matchInOrOf(&isForOf);
|
||||
if (isForInOrOf) {
|
||||
ParseNodeKind headKind = PNK_FORHEAD;
|
||||
if (pn1) {
|
||||
bool isForOf;
|
||||
if (matchInOrOf(&isForOf))
|
||||
headKind = isForOf ? PNK_FOROF : PNK_FORIN;
|
||||
}
|
||||
|
||||
if (headKind == PNK_FOROF || headKind == PNK_FORIN) {
|
||||
/*
|
||||
* Parse the rest of the for/in or for/of head.
|
||||
*
|
||||
|
@ -3993,17 +4000,20 @@ Parser<FullParseHandler>::forStatement()
|
|||
* that receives the enumeration value each iteration, and pn3 is the
|
||||
* rhs of 'in'.
|
||||
*/
|
||||
forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
|
||||
|
||||
/* Set iflags and rule out invalid combinations. */
|
||||
if (isForOf && isForEach) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
if (headKind == PNK_FOROF) {
|
||||
forStmt.type = STMT_FOR_OF_LOOP;
|
||||
forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
|
||||
if (isForEach) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
}
|
||||
} else {
|
||||
forStmt.type = STMT_FOR_IN_LOOP;
|
||||
iflags |= JSITER_ENUMERATE;
|
||||
}
|
||||
iflags |= (isForOf ? JSITER_FOR_OF : JSITER_ENUMERATE);
|
||||
|
||||
/* Check that the left side of the 'in' or 'of' is valid. */
|
||||
if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, isForOf)) {
|
||||
if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) {
|
||||
report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE);
|
||||
return null();
|
||||
}
|
||||
|
@ -4029,12 +4039,14 @@ Parser<FullParseHandler>::forStatement()
|
|||
* 'const' to hoist the initializer or the entire decl out of
|
||||
* the loop head.
|
||||
*/
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
if (headKind == PNK_FOROF) {
|
||||
report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT);
|
||||
return null();
|
||||
}
|
||||
if (blockObj) {
|
||||
report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
|
||||
return null();
|
||||
}
|
||||
#endif /* JS_HAS_BLOCK_SCOPE */
|
||||
|
||||
hoistedVar = pn1;
|
||||
|
||||
|
@ -4113,7 +4125,7 @@ Parser<FullParseHandler>::forStatement()
|
|||
* Destructuring for-in requires [key, value] enumeration
|
||||
* in JS1.7.
|
||||
*/
|
||||
if (!isForEach && !isForOf)
|
||||
if (!isForEach && headKind == PNK_FORIN)
|
||||
iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
|
||||
}
|
||||
break;
|
||||
|
@ -4127,6 +4139,8 @@ Parser<FullParseHandler>::forStatement()
|
|||
return null();
|
||||
}
|
||||
|
||||
headKind = PNK_FORHEAD;
|
||||
|
||||
if (blockObj) {
|
||||
/*
|
||||
* Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
|
||||
|
@ -4165,7 +4179,7 @@ Parser<FullParseHandler>::forStatement()
|
|||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
ParseNode *forHead = handler.newForHead(isForInOrOf, pn1, pn2, pn3, headPos);
|
||||
ParseNode *forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos);
|
||||
if (!forHead)
|
||||
return null();
|
||||
|
||||
|
@ -5933,13 +5947,15 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
|
||||
return null();
|
||||
}
|
||||
ParseNodeKind headKind = PNK_FORIN;
|
||||
if (isForOf) {
|
||||
if (pn2->pn_iflags != JSITER_ENUMERATE) {
|
||||
JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
|
||||
report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
|
||||
return null();
|
||||
}
|
||||
pn2->pn_iflags = JSITER_FOR_OF;
|
||||
pn2->pn_iflags = 0;
|
||||
headKind = PNK_FOROF;
|
||||
}
|
||||
|
||||
ParseNode *pn4 = expr();
|
||||
|
@ -5972,6 +5988,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
|
||||
JS_ASSERT(pn2->isOp(JSOP_ITER));
|
||||
JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
|
||||
JS_ASSERT(headKind == PNK_FORIN);
|
||||
pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
|
||||
}
|
||||
break;
|
||||
|
@ -6003,7 +6020,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
if (!pn3)
|
||||
return null();
|
||||
|
||||
pn2->pn_left = handler.newTernary(PNK_FORIN, vars, pn3, pn4);
|
||||
pn2->pn_left = handler.newTernary(headKind, vars, pn3, pn4);
|
||||
if (!pn2->pn_left)
|
||||
return null();
|
||||
*pnp = pn2;
|
||||
|
|
|
@ -578,8 +578,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
|
||||
bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
|
||||
|
||||
bool isValidForStatementLHS(Node pn1, JSVersion version,
|
||||
bool forDecl, bool forEach, bool forOf);
|
||||
bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
|
||||
ParseNodeKind headKind);
|
||||
bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
|
||||
bool checkStrictAssignment(Node lhs, AssignmentFlavor flavor);
|
||||
bool checkStrictBinding(PropertyName *name, Node pn);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// For-of can't have initializers.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function assertSyntaxError(str) {
|
||||
assertThrowsInstanceOf(function () { return Function(str); }, SyntaxError);
|
||||
}
|
||||
|
||||
assertSyntaxError("for (var x = 1 of []) {}");
|
||||
assertSyntaxError("for (var [x] = 1 of []) {}");
|
||||
assertSyntaxError("for (var {x} = 1 of []) {}");
|
||||
|
||||
assertSyntaxError("for (let x = 1 of []) {}");
|
||||
assertSyntaxError("for (let [x] = 1 of []) {}");
|
||||
assertSyntaxError("for (let {x} = 1 of []) {}");
|
||||
|
||||
assertSyntaxError("for (const x = 1 of []) {}");
|
||||
assertSyntaxError("for (const [x] = 1 of []) {}");
|
||||
assertSyntaxError("for (const {x} = 1 of []) {}");
|
|
@ -18,7 +18,7 @@ function test() {
|
|||
var a = [1, 2, 3];
|
||||
var s = '';
|
||||
for (var x of a)
|
||||
for (var i=0 of 'y')
|
||||
for (var i of 'y')
|
||||
s += '' + foo()
|
||||
} test();
|
||||
|
||||
|
@ -26,7 +26,7 @@ ignoreComments = [];
|
|||
|
||||
function bug909276() {
|
||||
var actual = '';
|
||||
for (var next = 0 of ignoreComments) {
|
||||
for (var next of ignoreComments) {
|
||||
actual += a;
|
||||
for (var b in x) {
|
||||
actual += b.eval("args = [-0, NaN, -1/0]; this.f(-0, NaN, -1/0);");
|
||||
|
|
|
@ -221,7 +221,7 @@ MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode J
|
|||
MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables")
|
||||
MSG_DEF(JSMSG_NEGATIVE_REPETITION_COUNT, 170, 0, JSEXN_RANGEERR, "repeat count must be non-negative")
|
||||
MSG_DEF(JSMSG_UNUSED171, 171, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 171, 0, JSEXN_SYNTAXERR, "for-of loop variable declaration may not have an initializer")
|
||||
MSG_DEF(JSMSG_UNUSED172, 172, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED173, 173, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED174, 174, 0, JSEXN_NONE, "")
|
||||
|
|
|
@ -639,7 +639,6 @@ ErrorFromException(JS::Value val);
|
|||
#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */
|
||||
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
|
||||
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
|
||||
#define JSITER_FOR_OF 0x20 /* harmony for-of loop */
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
RunningWithTrustedPrincipals(JSContext *cx);
|
||||
|
|
|
@ -564,37 +564,6 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
|
|||
bool
|
||||
js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
|
||||
{
|
||||
if (flags == JSITER_FOR_OF) {
|
||||
// for-of loop. The iterator is simply |obj[@@iterator]()|.
|
||||
RootedValue method(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().std_iterator, &method))
|
||||
return false;
|
||||
|
||||
// Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
|
||||
// for this kind of error anyway, but it would throw an inscrutable
|
||||
// error message about |method| rather than this nice one about |obj|.
|
||||
if (!method.isObject() || !method.toObject().isCallable()) {
|
||||
RootedValue val(cx, ObjectOrNullValue(obj));
|
||||
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
|
||||
if (!bytes)
|
||||
return false;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes);
|
||||
js_free(bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Invoke(cx, ObjectOrNullValue(obj), method, 0, nullptr, vp))
|
||||
return false;
|
||||
|
||||
JSObject *resultObj = ToObject(cx, vp);
|
||||
if (!resultObj)
|
||||
return false;
|
||||
vp.setObject(*resultObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(!(flags & JSITER_FOR_OF));
|
||||
|
||||
Vector<Shape *, 8> shapes(cx);
|
||||
uint32_t key = 0;
|
||||
|
||||
|
@ -1321,6 +1290,46 @@ const Class StopIterationObject::class_ = {
|
|||
nullptr /* construct */
|
||||
};
|
||||
|
||||
bool
|
||||
ForOfIterator::init(HandleValue iterable)
|
||||
{
|
||||
RootedObject iterableObj(cx, ToObject(cx, iterable));
|
||||
if (!iterableObj)
|
||||
return false;
|
||||
|
||||
// The iterator is the result of calling obj[@@iterator]().
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(0))
|
||||
return false;
|
||||
args.setThis(ObjectValue(*iterableObj));
|
||||
|
||||
RootedValue callee(cx);
|
||||
if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
|
||||
return false;
|
||||
|
||||
// Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
|
||||
// for this kind of error anyway, but it would throw an inscrutable
|
||||
// error message about |method| rather than this nice one about |obj|.
|
||||
if (!callee.isObject() || !callee.toObject().isCallable()) {
|
||||
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
|
||||
if (!bytes)
|
||||
return false;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes);
|
||||
js_free(bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
args.setCallee(callee);
|
||||
if (!Invoke(cx, args))
|
||||
return false;
|
||||
|
||||
iterator = ToObject(cx, args.rval());
|
||||
if (!iterator)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ForOfIterator::next(MutableHandleValue vp, bool *done)
|
||||
{
|
||||
|
|
|
@ -208,7 +208,7 @@ UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj);
|
|||
bool
|
||||
IteratorConstructor(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
}
|
||||
} /* namespace js */
|
||||
|
||||
extern bool
|
||||
js_SuppressDeletedProperty(JSContext *cx, js::HandleObject obj, jsid id);
|
||||
|
@ -265,14 +265,7 @@ class ForOfIterator
|
|||
public:
|
||||
ForOfIterator(JSContext *cx) : cx(cx), iterator(cx) { }
|
||||
|
||||
bool init(HandleValue iterable) {
|
||||
RootedValue iterv(cx, iterable);
|
||||
if (!ValueToIterator(cx, JSITER_FOR_OF, &iterv))
|
||||
return false;
|
||||
iterator = &iterv.get().toObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init(HandleValue iterable);
|
||||
bool next(MutableHandleValue val, bool *done);
|
||||
};
|
||||
|
||||
|
|
|
@ -1546,8 +1546,10 @@ class ASTSerializer
|
|||
}
|
||||
|
||||
bool forInit(ParseNode *pn, MutableHandleValue dst);
|
||||
bool forOfOrIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
MutableHandleValue dst);
|
||||
bool forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
MutableHandleValue dst);
|
||||
bool forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
MutableHandleValue dst);
|
||||
bool statement(ParseNode *pn, MutableHandleValue dst);
|
||||
bool blockStatement(ParseNode *pn, MutableHandleValue dst);
|
||||
bool switchStatement(ParseNode *pn, MutableHandleValue dst);
|
||||
|
@ -2018,17 +2020,24 @@ ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst)
|
|||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::forOfOrIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
ASTSerializer::forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
MutableHandleValue dst)
|
||||
{
|
||||
RootedValue expr(cx);
|
||||
|
||||
return expression(head->pn_kid3, &expr) &&
|
||||
builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt,
|
||||
MutableHandleValue dst)
|
||||
{
|
||||
RootedValue expr(cx);
|
||||
bool isForEach = loop->pn_iflags & JSITER_FOREACH;
|
||||
bool isForOf = loop->pn_iflags & JSITER_FOR_OF;
|
||||
JS_ASSERT(!isForOf || !isForEach);
|
||||
|
||||
return expression(head->pn_kid3, &expr) &&
|
||||
(isForOf ? builder.forOfStatement(var, expr, stmt, &loop->pn_pos, dst) :
|
||||
builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst));
|
||||
builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2139,7 +2148,17 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
|||
: head->pn_kid1->isKind(PNK_LEXICALSCOPE)
|
||||
? variableDeclaration(head->pn_kid1->pn_expr, true, &var)
|
||||
: variableDeclaration(head->pn_kid1, false, &var)) &&
|
||||
forOfOrIn(pn, head, var, stmt, dst);
|
||||
forIn(pn, head, var, stmt, dst);
|
||||
}
|
||||
|
||||
if (head->isKind(PNK_FOROF)) {
|
||||
RootedValue var(cx);
|
||||
return (!head->pn_kid1
|
||||
? pattern(head->pn_kid2, nullptr, &var)
|
||||
: head->pn_kid1->isKind(PNK_LEXICALSCOPE)
|
||||
? variableDeclaration(head->pn_kid1->pn_expr, true, &var)
|
||||
: variableDeclaration(head->pn_kid1, false, &var)) &&
|
||||
forOf(pn, head, var, stmt, dst);
|
||||
}
|
||||
|
||||
RootedValue init(cx), test(cx), update(cx);
|
||||
|
@ -2169,7 +2188,7 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
|||
|
||||
RootedValue stmt(cx);
|
||||
|
||||
return statement(loop->pn_right, &stmt) && forOfOrIn(loop, head, var, stmt, dst);
|
||||
return statement(loop->pn_right, &stmt) && forIn(loop, head, var, stmt, dst);
|
||||
}
|
||||
|
||||
case PNK_BREAK:
|
||||
|
@ -2262,10 +2281,10 @@ ASTSerializer::comprehensionBlock(ParseNode *pn, MutableHandleValue dst)
|
|||
|
||||
ParseNode *in = pn->pn_left;
|
||||
|
||||
LOCAL_ASSERT(in && in->isKind(PNK_FORIN));
|
||||
LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF)));
|
||||
|
||||
bool isForEach = pn->pn_iflags & JSITER_FOREACH;
|
||||
bool isForOf = pn->pn_iflags & JSITER_FOR_OF;
|
||||
bool isForOf = in->isKind(PNK_FOROF);
|
||||
|
||||
RootedValue patt(cx), src(cx);
|
||||
return pattern(in->pn_kid2, nullptr, &patt) &&
|
||||
|
|
|
@ -681,8 +681,6 @@ assertError("for each (const [x,y,z] in foo);", SyntaxError);
|
|||
|
||||
assertStmt("for (var {a:x,b:y,c:z} = 22 in foo);", forInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertStmt("for (var [x,y,z] = 22 in foo);", forInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertStmt("for (var {a:x,b:y,c:z} = 22 of foo);", forOfStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertStmt("for (var [x,y,z] = 22 of foo);", forOfStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertStmt("for each (var {a:x,b:y,c:z} = 22 in foo);", forEachInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertStmt("for each (var [x,y,z] = 22 in foo);", forEachInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
|
||||
assertError("for (x = 22 in foo);", SyntaxError);
|
||||
|
|
Загрузка…
Ссылка в новой задаче