diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-05-04 10:56:19 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-05-09 20:47:27 +0000 |
commit | 128485ccdb4f1c6f1bf1ebea85c5bed749cacd7a (patch) | |
tree | 350c795726910498f2a2ee7f9caa71018ae7ad5a /src/qml | |
parent | 20a5d1ab3f47b4da6ba39143beaafedb26dbad6a (diff) |
Add some basic support for for-of loops
The support is basically at the same level as for for-in
at the moment.
Currently unimplemented:
* Destructuring
* Proper lexical scoping
* calling iterator.throw()/return() when required
Change-Id: If193ce0b054c4315fc16b7e174334a31b2730dcf
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 16 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 8 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 6 | ||||
-rw-r--r-- | src/qml/jit/qv4jit.cpp | 24 | ||||
-rw-r--r-- | src/qml/jit/qv4jit_p.h | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4objectiterator_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 24 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 12 | ||||
-rw-r--r-- | src/qml/parser/qqmljs.g | 31 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast_p.h | 11 | ||||
-rw-r--r-- | src/qml/parser/qqmljskeywords_p.h | 5 | ||||
-rw-r--r-- | src/qml/parser/qqmljslexer.cpp | 1 |
15 files changed, 81 insertions, 73 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3e31693593..3543ddb177 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2593,11 +2593,20 @@ bool Codegen::visit(ForEachStatement *ast) return true; expr.loadInAccumulator(); - Instruction::ForeachIteratorObject iteratorObjInstr; + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = (ast->type == ForEachType::Of) ? 1 : 0; bytecodeGenerator->addInstruction(iteratorObjInstr); iterator.storeConsumeAccumulator(); - Reference lhs = expression(ast->initialiser).asLValue(); + Reference lhs = expression(ast->initialiser); + if (hasError) + return false; + if (!lhs.isLValue()) { + throwSyntaxError(ast->forToken, QLatin1String("Destructuring not supported with for-in and for-of loops.")); + return false; + } + + lhs = lhs.asLValue(); foreachBody(lhs, ast->statement, ast->forToken, iterator); return false; @@ -2618,7 +2627,8 @@ bool Codegen::visit(LocalForEachStatement *ast) variableDeclaration(ast->declaration); expr.loadInAccumulator(); - Instruction::ForeachIteratorObject iteratorObjInstr; + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = (ast->type == ForEachType::Of) ? 1 : 0; bytecodeGenerator->addInstruction(iteratorObjInstr); iterator.storeConsumeAccumulator(); diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 49d8455617..6ef4e79344 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -419,11 +419,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(reg, nFormals); MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) - MOTH_END_INSTR(ForeachIteratorObject) - - MOTH_BEGIN_INSTR(ForeachNextPropertyName) - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_BEGIN_INSTR(GetIterator) + d << iterator; + MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(DeleteMember) d << dumpRegister(base, nFormals) << "[" << member << "]"; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 2e3d118e52..546e8fd2de 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -118,8 +118,7 @@ QT_BEGIN_NAMESPACE #define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg) #define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 2, reg, index) #define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg) -#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0) -#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0) +#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator) #define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base) #define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index) #define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name) @@ -242,8 +241,7 @@ QT_BEGIN_NAMESPACE F(PushWithContext) \ F(PushBlockContext) \ F(PopContext) \ - F(ForeachIteratorObject) \ - F(ForeachNextPropertyName) \ + F(GetIterator) \ F(DeleteMember) \ F(DeleteSubscript) \ F(DeleteName) \ diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp index 87c3642479..16b6b5fb94 100644 --- a/src/qml/jit/qv4jit.cpp +++ b/src/qml/jit/qv4jit.cpp @@ -662,23 +662,14 @@ void BaselineJIT::generate_PushBlockContext(int reg, int index) void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); } -void BaselineJIT::generate_ForeachIteratorObject() +void BaselineJIT::generate_GetIterator(int iterator) { as->saveAccumulatorInFrame(); - as->prepareCallWithArgCount(2); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(iterator, 2); as->passAccumulatorAsArg(1); as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator); - as->checkException(); -} - -void BaselineJIT::generate_ForeachNextPropertyName() -{ - as->saveAccumulatorInFrame(); - as->prepareCallWithArgCount(1); - as->passAccumulatorAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName, - Assembler::ResultInAccumulator); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, Assembler::ResultInAccumulator); as->checkException(); } @@ -1220,11 +1211,8 @@ void BaselineJIT::collectLabelsInBytecode() MOTH_BEGIN_INSTR(PopContext) MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) - MOTH_END_INSTR(ForeachIteratorObject) - - MOTH_BEGIN_INSTR(ForeachNextPropertyName) - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_BEGIN_INSTR(GetIterator) + MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(DeleteMember) MOTH_END_INSTR(DeleteMember) diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h index bf11e69cbb..157fbffeb1 100644 --- a/src/qml/jit/qv4jit_p.h +++ b/src/qml/jit/qv4jit_p.h @@ -182,8 +182,7 @@ public: void generate_PushWithContext(int reg) override; void generate_PushBlockContext(int reg, int index) override; void generate_PopContext(int reg) override; - void generate_ForeachIteratorObject() override; - void generate_ForeachNextPropertyName() override; + void generate_GetIterator(int iterator) override; void generate_DeleteMember(int member, int base) override; void generate_DeleteSubscript(int base, int index) override; void generate_DeleteName(int name) override; diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index f96e3c87d8..bd22e2bc04 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -131,8 +131,8 @@ QString Managed::className() const case Type_StringIteratorObject: s = "String Iterator"; break; - case Type_ForeachIteratorObject: - s = "__ForeachIterator"; + case Type_ForInIterator: + s = "__ForIn Iterator"; break; case Type_InternalClass: s = "__InternalClass"; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 1b67aacd7b..020e61f014 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -201,7 +201,7 @@ public: Type_InternalClass, Type_ArrayIteratorObject, Type_StringIteratorObject, - Type_ForeachIteratorObject, + Type_ForInIterator, Type_RegExp, Type_QmlSequence diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 2b2410bf1f..462bd9a114 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -133,7 +133,7 @@ struct ForInIteratorPrototype : Object struct ForInIteratorObject: Object { V4_OBJECT2(ForInIteratorObject, Object) - Q_MANAGED_TYPE(ForeachIteratorObject) + Q_MANAGED_TYPE(ForInIterator) V4_PROTOTYPE(forInIteratorPrototype) ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 1c1c3c3802..a83edfaed6 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -695,26 +695,26 @@ bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, return setElementFallback(engine, object, index, value); } -ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) +ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); if (!in.isNullOrUndefined()) o = in.toObject(engine); + if (engine->hasException) + return Encode::undefined(); + if (iterator) { + if (!o) + return engine->throwTypeError(); + ScopedFunctionObject f(scope, o->get(engine->symbol_iterator())); + if (!f) + return engine->throwTypeError(); + JSCallData cData(scope, 0, nullptr, o); + return f->call(cData); + } return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator) -{ - Q_ASSERT(foreach_iterator.isObject()); - - ForInIteratorObject *it = static_cast<ForInIteratorObject *>(foreach_iterator.objectValue()); - Q_ASSERT(it->as<ForInIteratorObject>()); - - return it->nextPropertyName(); -} - - void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index ac456283cb..4d4112bf0c 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -141,9 +141,8 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \ \ - /* foreach */ \ - F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ - F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ + /* for-in and for-of */ \ + F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ \ /* unary operators */ \ F(ReturnedValue, uMinus, (const Value &value)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 08a085c90d..7f27619d08 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -938,17 +938,11 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const uchar *code) STACK_VALUE(CallData::Context) = STACK_VALUE(reg); MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_BEGIN_INSTR(GetIterator) STORE_ACC(); - acc = Runtime::method_foreachIterator(engine, accumulator); + acc = Runtime::method_getIterator(engine, accumulator, iterator); CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachIteratorObject) - - MOTH_BEGIN_INSTR(ForeachNextPropertyName) - STORE_ACC(); - acc = Runtime::method_foreachNextPropertyName(accumulator); - CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(DeleteMember) if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) { diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index c25f3395e2..8e71b8a42b 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -262,6 +262,7 @@ public: int ival; double dval; AST::VariableScope scope; + AST::ForEachType forEachType; AST::ArgumentList *ArgumentList; AST::CaseBlock *CaseBlock; AST::CaseClause *CaseClause; @@ -3125,37 +3126,47 @@ IterationStatement: T_FOR T_LPAREN LexicalDeclaration T_SEMICOLON ExpressionOpt_ } break; ./ -IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression_In T_RPAREN Statement; +InOrOf: T_IN; +/. + case $rule_number: { + sym(1).forEachType = AST::ForEachType::In; + } break; +./ + +InOrOf: T_OF; +/. + case $rule_number: { + sym(1).forEachType = AST::ForEachType::Of; + } break; +./ + +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression InOrOf Expression_In T_RPAREN Statement; /. case $rule_number: { AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); node->lparenToken = loc(2); - node->inToken = loc(4); + node->inOfToken = loc(4); node->rparenToken = loc(6); + node->type = sym(4).forEachType; sym(1).Node = node; } break; ./ -IterationStatement: T_FOR T_LPAREN ForDeclaration T_IN Expression_In T_RPAREN Statement; +IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN Statement; /. case $rule_number: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(sym(3).PatternElement, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); node->lparenToken = loc(2); node->varToken = loc(3); - node->inToken = loc(4); + node->inOfToken = loc(4); node->rparenToken = loc(6); + node->type = sym(4).forEachType; sym(1).Node = node; } break; ./ - -IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_OF AssignmentExpression_In T_RPAREN Statement; -- [lookahead ≠ let] -/. case $rule_number: { UNIMPLEMENTED; } ./ -IterationStatement: T_FOR T_LPAREN ForDeclaration T_OF Expression_In T_RPAREN Statement; -/. case $rule_number: { UNIMPLEMENTED; } ./ - ForDeclaration: LetOrConst ForBinding; /. case $rule_number: Q_FALLTHROUGH(); ./ ForDeclaration: Var ForBinding; diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index c83a459506..e54d1aea31 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -1701,6 +1701,11 @@ public: SourceLocation rparenToken; }; +enum class ForEachType { + In, + Of +}; + class QML_PARSER_EXPORT ForEachStatement: public Statement { public: @@ -1724,8 +1729,9 @@ public: Statement *statement; SourceLocation forToken; SourceLocation lparenToken; - SourceLocation inToken; + SourceLocation inOfToken; SourceLocation rparenToken; + ForEachType type; }; class QML_PARSER_EXPORT LocalForEachStatement: public Statement @@ -1752,8 +1758,9 @@ public: SourceLocation forToken; SourceLocation lparenToken; SourceLocation varToken; - SourceLocation inToken; + SourceLocation inOfToken; SourceLocation rparenToken; + ForEachType type; }; class QML_PARSER_EXPORT ContinueStatement: public Statement diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 9cc47469e5..40094ecdf8 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -76,10 +76,13 @@ static inline int classify2(const QChar *s, int parseModeFlags) { return Lexer::T_IN; } } - else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'o') { + else if (s[0].unicode() == 'o') { if (s[1].unicode() == 'n') { return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_ON : Lexer::T_IDENTIFIER; } + else if (s[1].unicode() == 'f') { + return Lexer::T_OF; + } } return Lexer::T_IDENTIFIER; } diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 3dbe535bcf..69e73f1374 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -1343,6 +1343,7 @@ static const int uriTokens[] = { QQmlJSGrammar::T_FUNCTION, QQmlJSGrammar::T_IF, QQmlJSGrammar::T_IN, + QQmlJSGrammar::T_OF, QQmlJSGrammar::T_INSTANCEOF, QQmlJSGrammar::T_NEW, QQmlJSGrammar::T_NULL, |