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