aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-05-04 10:56:19 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-09 20:47:27 +0000
commit128485ccdb4f1c6f1bf1ebea85c5bed749cacd7a (patch)
tree350c795726910498f2a2ee7f9caa71018ae7ad5a /src/qml
parent20a5d1ab3f47b4da6ba39143beaafedb26dbad6a (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.cpp16
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp8
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h6
-rw-r--r--src/qml/jit/qv4jit.cpp24
-rw-r--r--src/qml/jit/qv4jit_p.h3
-rw-r--r--src/qml/jsruntime/qv4managed.cpp4
-rw-r--r--src/qml/jsruntime/qv4managed_p.h2
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h2
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp24
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h5
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp12
-rw-r--r--src/qml/parser/qqmljs.g31
-rw-r--r--src/qml/parser/qqmljsast_p.h11
-rw-r--r--src/qml/parser/qqmljskeywords_p.h5
-rw-r--r--src/qml/parser/qqmljslexer.cpp1
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,