1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include <qv4generatorobject_p.h>
41 #include <qv4symbol_p.h>
42 #include <qv4iterator_p.h>
43 #include <qv4jscall_p.h>
44 #include <qv4vme_moth_p.h>
45
46 using namespace QV4;
47
48 DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor);
49 DEFINE_OBJECT_VTABLE(GeneratorFunction);
50 DEFINE_OBJECT_VTABLE(GeneratorObject);
51
init(QV4::ExecutionContext * scope)52 void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope)
53 {
54 Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction"));
55 }
56
virtualCallAsConstructor(const FunctionObject * f,const Value * argv,int argc,const Value * newTarget)57 ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
58 {
59 ExecutionEngine *engine = f->engine();
60
61 QQmlRefPointer<ExecutableCompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
62 if (engine->hasException)
63 return Encode::undefined();
64
65 Function *vmf = compilationUnit->linkToEngine(engine);
66 ExecutionContext *global = engine->scriptContext();
67 ReturnedValue o = Encode(GeneratorFunction::create(global, vmf));
68
69 if (!newTarget)
70 return o;
71 Scope scope(engine);
72 ScopedObject obj(scope, o);
73 obj->setProtoFromNewTarget(newTarget);
74 return obj->asReturnedValue();
75 }
76
77 // 15.3.1: This is equivalent to new Function(...)
virtualCall(const FunctionObject * f,const Value *,const Value * argv,int argc)78 ReturnedValue GeneratorFunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
79 {
80 return virtualCallAsConstructor(f, argv, argc, f);
81 }
82
create(ExecutionContext * context,Function * function)83 Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function)
84 {
85 Scope scope(context);
86 Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function));
87 ScopedObject proto(scope, scope.engine->newObject());
88 proto->setPrototypeOf(scope.engine->generatorPrototype());
89 g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
90 g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
91 return g->d();
92 }
93
virtualCall(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)94 ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
95 {
96 const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f);
97 Function *function = gf->function();
98 ExecutionEngine *engine = gf->engine();
99
100 // We need to set up a separate stack for the generator, as it's being re-entered
101 uint stackSize = argc // space for the original arguments
102 + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame
103
104 size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize;
105
106 Scope scope(gf);
107 Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
108 g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
109
110 Heap::GeneratorObject *gp = g->d();
111 gp->stack.size = stackSize;
112 gp->stack.alloc = stackSize;
113
114 // copy original arguments
115 memcpy(gp->stack.values, argv, argc*sizeof(Value));
116 gp->cppFrame.init(engine, function, gp->stack.values, argc);
117 gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(),
118 thisObject ? *thisObject : Value::undefinedValue(),
119 Value::undefinedValue());
120
121 gp->cppFrame.push();
122
123 Moth::VME::interpret(&gp->cppFrame, engine, function->codeData);
124 gp->state = GeneratorState::SuspendedStart;
125
126 gp->cppFrame.pop();
127 return g->asReturnedValue();
128 }
129
130
init()131 void Heap::GeneratorPrototype::init()
132 {
133 Heap::FunctionObject::init();
134 }
135
136
init(ExecutionEngine * engine,Object * ctor)137 void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
138 {
139 Scope scope(engine);
140 ScopedValue v(scope);
141
142 Scoped<InternalClass> ic(scope, engine->newInternalClass(
143 Object::staticVTable(), engine->functionPrototype()));
144 ScopedObject ctorProto(scope, engine->newObject(ic->d()));
145
146 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
147 ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
148
149 ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable);
150 ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable);
151 ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable);
152
153 setPrototypeOf(engine->iteratorPrototype());
154 defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable);
155 defineDefaultProperty(QStringLiteral("next"), method_next, 1);
156 defineDefaultProperty(QStringLiteral("return"), method_return, 1);
157 defineDefaultProperty(QStringLiteral("throw"), method_throw, 1);
158 defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable);
159 }
160
method_next(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)161 ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
162 {
163 ExecutionEngine *engine = f->engine();
164 const GeneratorObject *g = thisObject->as<GeneratorObject>();
165 if (!g || g->d()->state == GeneratorState::Executing)
166 return engine->throwTypeError();
167 Heap::GeneratorObject *gp = g->d();
168
169 if (gp->state == GeneratorState::Completed)
170 return IteratorPrototype::createIterResultObject(engine, Value::undefinedValue(), true);
171
172 return g->resume(engine, argc ? argv[0] : Value::undefinedValue());
173 }
174
method_return(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)175 ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
176 {
177 ExecutionEngine *engine = f->engine();
178 const GeneratorObject *g = thisObject->as<GeneratorObject>();
179 if (!g || g->d()->state == GeneratorState::Executing)
180 return engine->throwTypeError();
181
182 Heap::GeneratorObject *gp = g->d();
183
184 if (gp->state == GeneratorState::SuspendedStart)
185 gp->state = GeneratorState::Completed;
186
187 if (gp->state == GeneratorState::Completed)
188 return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Value::undefinedValue(), true);
189
190 // the bytecode interpreter interprets an exception with empty value as
191 // a yield called with return()
192 engine->throwError(Value::emptyValue());
193
194 return g->resume(engine, argc ? argv[0]: Value::undefinedValue());
195 }
196
method_throw(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)197 ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
198 {
199 ExecutionEngine *engine = f->engine();
200 const GeneratorObject *g = thisObject->as<GeneratorObject>();
201 if (!g || g->d()->state == GeneratorState::Executing)
202 return engine->throwTypeError();
203
204 Heap::GeneratorObject *gp = g->d();
205
206 engine->throwError(argc ? argv[0]: Value::undefinedValue());
207
208 if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) {
209 gp->state = GeneratorState::Completed;
210 return Encode::undefined();
211 }
212
213 return g->resume(engine, Value::undefinedValue());
214 }
215
resume(ExecutionEngine * engine,const Value & arg) const216 ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const
217 {
218 Heap::GeneratorObject *gp = d();
219 gp->state = GeneratorState::Executing;
220 gp->cppFrame.parent = engine->currentStackFrame;
221 engine->currentStackFrame = &gp->cppFrame;
222
223 Q_ASSERT(gp->cppFrame.yield != nullptr);
224 const char *code = gp->cppFrame.yield;
225 gp->cppFrame.yield = nullptr;
226 gp->cppFrame.jsFrame->accumulator = arg;
227 gp->cppFrame.yieldIsIterator = false;
228
229 Scope scope(engine);
230 ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code));
231
232 engine->currentStackFrame = gp->cppFrame.parent;
233
234 bool done = (gp->cppFrame.yield == nullptr);
235 gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield;
236 if (engine->hasException)
237 return Encode::undefined();
238 if (gp->cppFrame.yieldIsIterator)
239 return result->asReturnedValue();
240 return IteratorPrototype::createIterResultObject(engine, result, done);
241 }
242
243 DEFINE_OBJECT_VTABLE(MemberGeneratorFunction);
244
create(ExecutionContext * context,Function * function,Object * homeObject,String * name)245 Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function, Object *homeObject, String *name)
246 {
247 Scope scope(context);
248 Scoped<MemberGeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function, name));
249 g->d()->homeObject.set(scope.engine, homeObject->d());
250 ScopedObject proto(scope, scope.engine->newObject());
251 proto->setPrototypeOf(scope.engine->generatorPrototype());
252 g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
253 g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
254 return g->d();
255 }
256
virtualCall(const FunctionObject * f,const Value * thisObject,const Value * argv,int argc)257 ReturnedValue MemberGeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
258 {
259 return GeneratorFunction::virtualCall(f, thisObject, argv, argc);
260 }
261