From 0363f0f4f8870db20c630cd2af94e0b05255d036 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 6 Jun 2018 13:55:10 +0200 Subject: Add support for spread expressions in Array literals Change-Id: I613d853dbb34d86ebedd871e9676d3206f1e3349 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 170 +++++++++++++++++++++++++++++++-------- src/qml/jsruntime/qv4runtime.cpp | 5 +- 2 files changed, 140 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b3b522e661..e39bb61688 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -831,53 +831,155 @@ bool Codegen::visit(ArrayPattern *ast) if (hasError) return false; - RegisterScope scope(this); + PatternElementList *it = ast->elements; int argc = 0; - int args = -1; - auto push = [this, &argc, &args](AST::ExpressionNode *arg) { - int temp = bytecodeGenerator->newRegister(); - if (args == -1) - args = temp; - if (!arg) { - auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); - (void) c.storeOnStack(temp); - } else { - RegisterScope scope(this); - Reference r = expression(arg); + { + RegisterScope scope(this); + + int args = -1; + auto push = [this, &argc, &args](AST::ExpressionNode *arg) { + int temp = bytecodeGenerator->newRegister(); + if (args == -1) + args = temp; + if (!arg) { + auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()); + (void) c.storeOnStack(temp); + } else { + RegisterScope scope(this); + Reference r = expression(arg); + if (hasError) + return; + (void) r.storeOnStack(temp); + } + ++argc; + }; + + for (; it; it = it->next) { + PatternElement *e = it->element; + if (e && e->type == PatternElement::SpreadElement) + break; + for (Elision *elision = it->elision; elision; elision = elision->next) + push(nullptr); + + if (!e) + continue; + + push(e->initializer); if (hasError) - return; - (void) r.storeOnStack(temp); + return false; } - ++argc; + + if (args == -1) { + Q_ASSERT(argc == 0); + args = 0; + } + + Instruction::DefineArray call; + call.argc = argc; + call.args = Moth::StackSlot::createRegister(args); + bytecodeGenerator->addInstruction(call); + } + + if (!it) { + _expr.setResult(Reference::fromAccumulator(this)); + return false; + } + Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement); + + RegisterScope scope(this); + Reference array = Reference::fromStackSlot(this); + array.storeConsumeAccumulator(); + Reference index = Reference::storeConstOnStack(this, Encode(argc)); + + auto pushAccumulator = [&]() { + Reference slot = Reference::fromSubscript(array, index); + slot.storeConsumeAccumulator(); + + index.loadInAccumulator(); + Instruction::Increment inc; + bytecodeGenerator->addInstruction(inc); + index.storeConsumeAccumulator(); }; - for (PatternElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - push(nullptr); + while (it) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator(); + pushAccumulator(); + } - PatternElement *e = it->element; - if (!e) + if (!it->element) { + it = it->next; continue; - if (e->type == PatternElement::RestElement) { - throwSyntaxError(it->firstSourceLocation(), QLatin1String("'...' in ArrayLiterals is unimplementd.")); - return false; } - push(e->initializer); - if (hasError) - return false; - } + // handle spread element + if (it->element->type == PatternElement::SpreadElement) { + RegisterScope scope(this); - if (args == -1) { - Q_ASSERT(argc == 0); - args = 0; + Reference iterator = Reference::fromStackSlot(this); + Reference lhsValue = Reference::fromStackSlot(this); + + // There should be a temporal block, so that variables declared in lhs shadow outside vars. + // This block should define a temporal dead zone for those variables, which is not yet implemented. + { + RegisterScope innerScope(this); + Reference expr = expression(it->element->initializer); + if (hasError) + return false; + + expr.loadInAccumulator(); + Instruction::GetIterator iteratorObjInstr; + iteratorObjInstr.iterator = /*ForEachType::Of*/ 1; + bytecodeGenerator->addInstruction(iteratorObjInstr); + iterator.storeConsumeAccumulator(); + } + + BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label done = bytecodeGenerator->newLabel(); + + { + ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true); + bytecodeGenerator->jump().link(in); + + BytecodeGenerator::Label body = bytecodeGenerator->label(); + + lhsValue.loadInAccumulator(); + pushAccumulator(); + + in.link(); + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + next.value = lhsValue.stackSlot(); + bytecodeGenerator->addInstruction(next); + bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); + bytecodeGenerator->jump().link(done); + } + + end.link(); + + Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); + iterator.loadInAccumulator(); + Instruction::IteratorClose close; + close.done = iteratorDone.stackSlot(); + bytecodeGenerator->addInstruction(close); + + done.link(); + } else { + RegisterScope innerScope(this); + Reference expr = expression(it->element->initializer); + if (hasError) + return false; + + expr.loadInAccumulator(); + pushAccumulator(); + } + + it = it->next; } - Instruction::DefineArray call; - call.argc = argc; - call.args = Moth::StackSlot::createRegister(args); - bytecodeGenerator->addInstruction(call); + array.loadInAccumulator(); _expr.setResult(Reference::fromAccumulator(this)); return false; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 73c579f3eb..b879e3fc14 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -745,7 +745,10 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value & if (!f) return engine->throwTypeError(); JSCallData cData(scope, 0, nullptr, o); - return f->call(cData); + ScopedObject it(scope, f->call(cData)); + if (!it) + return engine->throwTypeError(); + return it->asReturnedValue(); } return engine->newForInIteratorObject(o)->asReturnedValue(); } -- cgit v1.2.3