diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-05-31 16:06:34 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-09-07 10:32:01 +0000 |
commit | ff5bc526b4868aef9fb3a551afc5636b308a3d83 (patch) | |
tree | 692e956d6fcfd5fc4f064dce7275abeb51429c3a /src/qml/jsruntime | |
parent | bd4064eabf79a6166805c877ee622931df6fb172 (diff) |
Add support for yield*
Change-Id: I5b054b59519ed825459a5b0b0a7cd2c6fc8a3797
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject.cpp | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 72 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stackframe_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 19 |
7 files changed, 97 insertions, 3 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 7b1fd720a7..8747676685 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -286,6 +286,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStrings[String_next] = newIdentifier(QStringLiteral("next")); jsStrings[String_done] = newIdentifier(QStringLiteral("done")); jsStrings[String_return] = newIdentifier(QStringLiteral("return")); + jsStrings[String_throw] = newIdentifier(QStringLiteral("throw")); jsStrings[String_global] = newIdentifier(QStringLiteral("global")); jsStrings[String_ignoreCase] = newIdentifier(QStringLiteral("ignoreCase")); jsStrings[String_multiline] = newIdentifier(QStringLiteral("multiline")); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 0b5d8663eb..276efe6e13 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -337,6 +337,7 @@ public: String_next, String_done, String_return, + String_throw, String_global, String_ignoreCase, String_multiline, @@ -408,6 +409,7 @@ public: String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); } String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); } String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); } + String *id_throw() const { return reinterpret_cast<String *>(jsStrings + String_throw); } String *id_global() const { return reinterpret_cast<String *>(jsStrings + String_global); } String *id_ignoreCase() const { return reinterpret_cast<String *>(jsStrings + String_ignoreCase); } String *id_multiline() const { return reinterpret_cast<String *>(jsStrings + String_multiline); } diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 82d8d88882..dd3d0328b0 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -215,6 +215,7 @@ ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const char *code = gp->cppFrame.yield; gp->cppFrame.yield = nullptr; gp->cppFrame.jsFrame->accumulator = arg; + gp->cppFrame.yieldIsIterator = false; Scope scope(engine); ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code)); @@ -225,6 +226,8 @@ ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield; if (engine->hasException) return Encode::undefined(); + if (gp->cppFrame.yieldIsIterator) + return result->asReturnedValue(); return IteratorPrototype::createIterResultObject(engine, result, done); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 59fff91e7b..b59b9c3f50 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -782,6 +782,78 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value return Encode(false); } +ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) +{ + // the return value encodes how to continue the yield* iteration. + // true implies iteration is done, false for iteration to continue + // a return value of undefines is a special marker, that the iterator has been invoked with return() + + Scope scope(engine); + Q_ASSERT(iterator.isObject()); + + const Value *arg = &received; + bool returnCalled = false; + FunctionObject *f = nullptr; + if (engine->hasException) { + if (engine->exceptionValue->isEmpty()) { + // generator called with return() + *engine->exceptionValue = Encode::undefined(); + engine->hasException = false; + + ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return())); + if (ret->isUndefined()) { + // propagate return() + return Encode::undefined(); + } + returnCalled = true; + f = ret->as<FunctionObject>(); + } else { + // generator called with throw + ScopedValue exceptionValue(scope, *engine->exceptionValue); + *engine->exceptionValue = Encode::undefined(); + engine->hasException = false; + + ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw())); + if (engine->hasException) + return Encode::undefined(); + if (t->isUndefined()) { + // no throw method on the iterator + ScopedValue done(scope, Encode(false)); + method_iteratorClose(engine, iterator, done); + if (engine->hasException) + return Encode::undefined(); + return engine->throwTypeError(); + } + f = t->as<FunctionObject>(); + arg = exceptionValue; + } + } else { + // generator called with next() + ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(engine->id_next())); + f = next->as<FunctionObject>(); + } + + if (!f) + return engine->throwTypeError(); + + ScopedObject o(scope, f->call(&iterator, arg, 1)); + if (scope.hasException()) + return Encode(true); + if (!o) + return engine->throwTypeError(); + + ScopedValue d(scope, o->get(engine->id_done())); + if (scope.hasException()) + return Encode(true); + bool done = d->toBoolean(); + if (done) { + *object = o->get(engine->id_value()); + return returnCalled ? Encode::undefined() : Encode(true); + } + *object = o; + return Encode(false); +} + ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) { Q_ASSERT(iterator.isObject()); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 62ba85efde..34b04929af 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -153,6 +153,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { /* for-in, for-of and array destructuring */ \ F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \ + F(ReturnedValue, iteratorNextForYieldStar, (ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)) \ F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \ F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \ \ diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index ca39a8f7bb..39e7c25a4e 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -122,6 +122,7 @@ struct Q_QML_EXPORT CppStackFrame { const char *unwindHandler; const char *unwindLabel; int unwindLevel; + bool yieldIsIterator; void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc) { this->engine = engine; @@ -134,6 +135,7 @@ struct Q_QML_EXPORT CppStackFrame { unwindHandler = nullptr; unwindLabel = nullptr; unwindLevel = 0; + yieldIsIterator = false; } void push() { diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 6ed371bbf2..07eb054350 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -661,9 +661,16 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Yield) frame->yield = code; + frame->yieldIsIterator = false; return acc; MOTH_END_INSTR(Yield) + MOTH_BEGIN_INSTR(YieldStar) + frame->yield = code; + frame->yieldIsIterator = true; + return acc; + MOTH_END_INSTR(YieldStar) + MOTH_BEGIN_INSTR(Resume) // check exception, in case the generator was called with throw() or return() if (engine->hasException) { @@ -672,11 +679,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, goto handleUnwind; engine->hasException = false; *engine->exceptionValue = Primitive::undefinedValue(); - } else { - code += offset; - } + } else { + code += offset; + } MOTH_END_INSTR(Resume) + MOTH_BEGIN_INSTR(IteratorNextForYieldStar) + STORE_ACC(); + acc = Runtime::method_iteratorNextForYieldStar(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); + CHECK_EXCEPTION; + MOTH_END_INSTR(IteratorNextForYieldStar) + MOTH_BEGIN_INSTR(CallValue) STORE_IP(); Value func = STACK_VALUE(name); |