aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-05-31 16:06:34 +0200
committerLars Knoll <lars.knoll@qt.io>2018-09-07 10:32:01 +0000
commitff5bc526b4868aef9fb3a551afc5636b308a3d83 (patch)
tree692e956d6fcfd5fc4f064dce7275abeb51429c3a /src/qml/jsruntime
parentbd4064eabf79a6166805c877ee622931df6fb172 (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.cpp1
-rw-r--r--src/qml/jsruntime/qv4engine_p.h2
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp72
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp19
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);