1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qv4object_p.h" |
5 | #include "qv4function_p.h" |
6 | #include "qv4symbol_p.h" |
7 | #include <private/qv4mm_p.h> |
8 | |
9 | #include "qv4scopedvalue_p.h" |
10 | #include "qv4argumentsobject_p.h" |
11 | |
12 | #include <private/qqmljsengine_p.h> |
13 | #include <private/qqmljslexer_p.h> |
14 | #include <private/qqmljsparser_p.h> |
15 | #include <private/qqmljsast_p.h> |
16 | #include <private/qqmljavascriptexpression_p.h> |
17 | #include <private/qqmlengine_p.h> |
18 | #include <qv4runtimecodegen_p.h> |
19 | #include "private/qlocale_tools_p.h" |
20 | #include "private/qqmlbuiltinfunctions_p.h" |
21 | #include <private/qv4jscall_p.h> |
22 | #include <private/qv4vme_moth_p.h> |
23 | #include <private/qv4alloca_p.h> |
24 | |
25 | #include <QtCore/QDebug> |
26 | #include <algorithm> |
27 | |
28 | using namespace QV4; |
29 | |
30 | |
31 | DEFINE_OBJECT_VTABLE(FunctionObject); |
32 | |
33 | void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, |
34 | VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes) |
35 | { |
36 | jsCall = call; |
37 | jsCallWithMetaTypes = callWithMetaTypes; |
38 | jsConstruct = nullptr; |
39 | |
40 | Object::init(); |
41 | this->scope.set(e: scope->engine(), newVal: scope->d()); |
42 | Scope s(scope->engine()); |
43 | ScopedFunctionObject f(s, this); |
44 | if (name) |
45 | f->setName(name); |
46 | } |
47 | |
48 | void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name) |
49 | { |
50 | ExecutionEngine *e = scope->engine(); |
51 | |
52 | jsCall = vtable()->call; |
53 | jsCallWithMetaTypes = vtable()->callWithMetaTypes; |
54 | jsConstruct = vtable()->callAsConstructor; |
55 | |
56 | Object::init(); |
57 | this->scope.set(e: scope->engine(), newVal: scope->d()); |
58 | Scope s(e); |
59 | ScopedFunctionObject f(s, this); |
60 | if (name) |
61 | f->setName(name); |
62 | } |
63 | |
64 | |
65 | |
66 | void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) |
67 | { |
68 | jsCall = vtable()->call; |
69 | jsCallWithMetaTypes = vtable()->callWithMetaTypes; |
70 | jsConstruct = vtable()->callAsConstructor; |
71 | |
72 | Object::init(); |
73 | setFunction(function); |
74 | this->scope.set(e: scope->engine(), newVal: scope->d()); |
75 | Scope s(scope->engine()); |
76 | ScopedString name(s, n ? n->d() : function->name()); |
77 | ScopedFunctionObject f(s, this); |
78 | if (name) |
79 | f->setName(name); |
80 | } |
81 | |
82 | void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name) |
83 | { |
84 | Scope valueScope(scope); |
85 | ScopedString s(valueScope, valueScope.engine->newString(s: name)); |
86 | init(scope, name: s); |
87 | } |
88 | |
89 | void Heap::FunctionObject::init() |
90 | { |
91 | jsCall = vtable()->call; |
92 | jsCallWithMetaTypes = vtable()->callWithMetaTypes; |
93 | jsConstruct = vtable()->callAsConstructor; |
94 | |
95 | Object::init(); |
96 | this->scope.set(e: internalClass->engine, newVal: internalClass->engine->rootContext()->d()); |
97 | } |
98 | |
99 | void Heap::FunctionObject::setFunction(Function *f) |
100 | { |
101 | if (f) { |
102 | function = f; |
103 | function->executableCompilationUnit()->addref(); |
104 | } |
105 | } |
106 | void Heap::FunctionObject::destroy() |
107 | { |
108 | if (function) |
109 | function->executableCompilationUnit()->release(); |
110 | Object::destroy(); |
111 | } |
112 | |
113 | void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) |
114 | { |
115 | Scope s(this); |
116 | |
117 | Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->verifyIndex(s.engine->id_constructor()->propertyKey(), protoConstructorSlot)); |
118 | |
119 | ScopedObject proto(s, s.engine->newObject(internalClass: s.engine->internalClasses(icType: EngineBase::Class_ObjectProto))); |
120 | proto->setProperty(index: protoConstructorSlot, b: d()); |
121 | defineDefaultProperty(name: s.engine->id_prototype(), value: proto, attributes: Attr_NotEnumerable|Attr_NotConfigurable); |
122 | } |
123 | |
124 | void FunctionObject::call(QObject *thisObject, void **a, const QMetaType *types, int argc) |
125 | { |
126 | if (const auto callWithMetaTypes = d()->jsCallWithMetaTypes) { |
127 | callWithMetaTypes(this, thisObject, a, types, argc); |
128 | return; |
129 | } |
130 | |
131 | QV4::convertAndCall(engine: engine(), thisObject, a, types, argc, |
132 | call: [this](const Value *thisObject, const Value *argv, int argc) { |
133 | return call(thisObject, argv, argc); |
134 | }); |
135 | } |
136 | |
137 | ReturnedValue FunctionObject::name() const |
138 | { |
139 | return get(name: scope()->internalClass->engine->id_name()); |
140 | } |
141 | |
142 | ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) |
143 | { |
144 | return Encode::undefined(); |
145 | } |
146 | |
147 | void FunctionObject::virtualCallWithMetaTypes( |
148 | const FunctionObject *, QObject *, void **, const QMetaType *, int) |
149 | { |
150 | } |
151 | |
152 | Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) |
153 | { |
154 | if (function->isArrowFunction()) |
155 | return scope->engine()->memoryManager->allocate<ArrowFunction>(args&: scope, args&: function); |
156 | return scope->engine()->memoryManager->allocate<ScriptFunction>(args&: scope, args&: function); |
157 | } |
158 | |
159 | Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) |
160 | { |
161 | if (!function) { |
162 | Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(args&: scope); |
163 | c->isDerivedConstructor = isDerivedConstructor; |
164 | return c; |
165 | } |
166 | Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(args&: scope, args&: function); |
167 | c->homeObject.set(e: scope->engine(), newVal: homeObject->d()); |
168 | c->isDerivedConstructor = isDerivedConstructor; |
169 | return c; |
170 | } |
171 | |
172 | Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, QV4::String *name) |
173 | { |
174 | Heap::MemberFunction *m = scope->engine()->memoryManager->allocate<MemberFunction>(args&: scope, args&: function, args&: name); |
175 | m->homeObject.set(e: scope->engine(), newVal: homeObject->d()); |
176 | return m; |
177 | } |
178 | |
179 | Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount) |
180 | { |
181 | Scope scope(engine); |
182 | ScopedString name(scope, nameOrSymbol); |
183 | if (!name) |
184 | name = engine->newString(s: QChar::fromLatin1(c: '[') + QStringView{nameOrSymbol->toQString()}.mid(pos: 1) + QChar::fromLatin1(c: ']')); |
185 | |
186 | ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(args: engine->rootContext(), args&: name, args&: code)); |
187 | function->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: argumentCount)); |
188 | return function->d(); |
189 | } |
190 | |
191 | ReturnedValue FunctionObject::getHomeObject() const |
192 | { |
193 | const MemberFunction *m = as<MemberFunction>(); |
194 | if (m) |
195 | return m->d()->homeObject->asReturnedValue(); |
196 | const ConstructorFunction *c = as<ConstructorFunction>(); |
197 | if (c) |
198 | return c->d()->homeObject->asReturnedValue(); |
199 | return Encode::undefined(); |
200 | } |
201 | |
202 | QQmlSourceLocation FunctionObject::sourceLocation() const |
203 | { |
204 | return d()->function->sourceLocation(); |
205 | } |
206 | |
207 | DEFINE_OBJECT_VTABLE(FunctionCtor); |
208 | |
209 | void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) |
210 | { |
211 | Heap::FunctionObject::init(scope, QStringLiteral("Function" )); |
212 | } |
213 | |
214 | // 15.3.2 |
215 | QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) |
216 | { |
217 | QString arguments; |
218 | QString body; |
219 | if (argc > 0) { |
220 | for (int i = 0, ei = argc - 1; i < ei; ++i) { |
221 | if (i) |
222 | arguments += QLatin1String(", " ); |
223 | arguments += argv[i].toQString(); |
224 | } |
225 | body = argv[argc - 1].toQString(); |
226 | } |
227 | if (engine->hasException) |
228 | return nullptr; |
229 | |
230 | QString function = (t == Type_Function ? QLatin1String("function anonymous(" ) : QLatin1String("function* anonymous(" )) + arguments + QLatin1String("\n){" ) + body + QLatin1String("\n}" ); |
231 | |
232 | QQmlJS::Engine ee; |
233 | QQmlJS::Lexer lexer(&ee); |
234 | lexer.setCode(code: function, lineno: 1, qmlMode: false); |
235 | QQmlJS::Parser parser(&ee); |
236 | |
237 | const bool parsed = parser.parseExpression(); |
238 | |
239 | if (!parsed) { |
240 | engine->throwSyntaxError(message: QLatin1String("Parse error" )); |
241 | return nullptr; |
242 | } |
243 | |
244 | QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); |
245 | if (!fe) { |
246 | engine->throwSyntaxError(message: QLatin1String("Parse error" )); |
247 | return nullptr; |
248 | } |
249 | |
250 | Compiler::Module module(engine->debugger() != nullptr); |
251 | |
252 | Compiler::JSUnitGenerator jsGenerator(&module); |
253 | RuntimeCodegen cg(engine, &jsGenerator, false); |
254 | cg.generateFromFunctionExpression(fileName: QString(), sourceCode: function, ast: fe, module: &module); |
255 | |
256 | if (engine->hasException) |
257 | return nullptr; |
258 | |
259 | return ExecutableCompilationUnit::create(compilationUnit: cg.generateCompilationUnit()); |
260 | } |
261 | |
262 | ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
263 | { |
264 | ExecutionEngine *engine = f->engine(); |
265 | |
266 | QQmlRefPointer<ExecutableCompilationUnit> compilationUnit |
267 | = parse(engine, argv, argc, t: Type_Function); |
268 | if (engine->hasException) |
269 | return Encode::undefined(); |
270 | |
271 | Function *vmf = compilationUnit->linkToEngine(engine); |
272 | ExecutionContext *global = engine->scriptContext(); |
273 | ReturnedValue o = Encode(FunctionObject::createScriptFunction(scope: global, function: vmf)); |
274 | |
275 | if (!newTarget) |
276 | return o; |
277 | Scope scope(engine); |
278 | ScopedObject obj(scope, o); |
279 | obj->setProtoFromNewTarget(newTarget); |
280 | return obj->asReturnedValue(); |
281 | } |
282 | |
283 | // 15.3.1: This is equivalent to new Function(...) |
284 | ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) |
285 | { |
286 | return virtualCallAsConstructor(f, argv, argc, newTarget: f); |
287 | } |
288 | |
289 | DEFINE_OBJECT_VTABLE(FunctionPrototype); |
290 | |
291 | void Heap::FunctionPrototype::init() |
292 | { |
293 | Heap::FunctionObject::init(); |
294 | } |
295 | |
296 | void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) |
297 | { |
298 | Scope scope(engine); |
299 | ScopedObject o(scope); |
300 | |
301 | ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 1)); |
302 | ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this)); |
303 | |
304 | defineReadonlyConfigurableProperty(name: engine->id_name(), value: *engine->id_empty()); |
305 | defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0)); |
306 | defineDefaultProperty(QStringLiteral("constructor" ), value: (o = ctor)); |
307 | defineDefaultProperty(name: engine->id_toString(), code: method_toString, argumentCount: 0); |
308 | defineDefaultProperty(QStringLiteral("apply" ), code: method_apply, argumentCount: 2); |
309 | defineDefaultProperty(QStringLiteral("call" ), code: method_call, argumentCount: 1); |
310 | defineDefaultProperty(QStringLiteral("bind" ), code: method_bind, argumentCount: 1); |
311 | defineDefaultProperty(name: engine->symbol_hasInstance(), code: method_hasInstance, argumentCount: 1, attributes: Attr_ReadOnly); |
312 | } |
313 | |
314 | ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
315 | { |
316 | ExecutionEngine *v4 = b->engine(); |
317 | const FunctionObject *fun = thisObject->as<FunctionObject>(); |
318 | if (!fun) |
319 | return v4->throwTypeError(); |
320 | |
321 | const Scope scope(fun->engine()); |
322 | const ScopedString scopedFunctionName(scope, fun->name()); |
323 | const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString()); |
324 | QString functionAsString = QStringLiteral("function" ); |
325 | |
326 | // If fun->name() is empty, then there is no function name |
327 | // to append because the function is anonymous. |
328 | if (!functionName.isEmpty()) |
329 | functionAsString.append(s: QLatin1Char(' ') + functionName); |
330 | |
331 | functionAsString.append(QStringLiteral("() { [native code] }" )); |
332 | |
333 | return Encode(v4->newString(s: functionAsString)); |
334 | } |
335 | |
336 | ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
337 | { |
338 | ExecutionEngine *v4 = b->engine(); |
339 | const FunctionObject *f = thisObject->as<FunctionObject>(); |
340 | if (!f) |
341 | return v4->throwTypeError(); |
342 | thisObject = argc ? argv : nullptr; |
343 | if (argc < 2 || argv[1].isNullOrUndefined()) |
344 | return checkedResult(v4, result: f->call(thisObject, argv, argc: 0)); |
345 | |
346 | Object *arr = argv[1].objectValue(); |
347 | if (!arr) |
348 | return v4->throwTypeError(); |
349 | |
350 | Scope scope(v4); |
351 | const int len = v4->safeForAllocLength(len64: arr->getLength()); |
352 | CHECK_EXCEPTION(); |
353 | |
354 | Value *arguments = scope.alloc<Scope::Uninitialized>(nValues: len); |
355 | if (len) { |
356 | if (ArgumentsObject::isNonStrictArgumentsObject(m: arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { |
357 | QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); |
358 | int l = qMin(a: len, b: a->d()->context->argc()); |
359 | memcpy(dest: arguments, src: a->d()->context->args(), n: l*sizeof(Value)); |
360 | for (int i = l; i < len; ++i) |
361 | arguments[i] = Value::undefinedValue(); |
362 | } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { |
363 | auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); |
364 | int alen = sad ? sad->values.size : 0; |
365 | if (alen > len) |
366 | alen = len; |
367 | for (int i = 0; i < alen; ++i) |
368 | arguments[i] = sad->data(index: i); |
369 | for (int i = alen; i < len; ++i) |
370 | arguments[i] = Value::undefinedValue(); |
371 | } else { |
372 | // need to init the arguments array, as the get() calls below can have side effects |
373 | memset(s: arguments, c: 0, n: len*sizeof(Value)); |
374 | for (int i = 0; i < len; ++i) |
375 | arguments[i] = arr->get(idx: i); |
376 | } |
377 | } |
378 | |
379 | return checkedResult(v4, result: f->call(thisObject, argv: arguments, argc: len)); |
380 | } |
381 | |
382 | ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
383 | { |
384 | QV4::ExecutionEngine *v4 = b->engine(); |
385 | if (!thisObject->isFunctionObject()) |
386 | return v4->throwTypeError(); |
387 | |
388 | const FunctionObject *f = static_cast<const FunctionObject *>(thisObject); |
389 | |
390 | thisObject = argc ? argv : nullptr; |
391 | if (argc) { |
392 | ++argv; |
393 | --argc; |
394 | } |
395 | return checkedResult(v4, result: f->call(thisObject, argv, argc)); |
396 | } |
397 | |
398 | ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
399 | { |
400 | QV4::Scope scope(b); |
401 | ScopedFunctionObject target(scope, thisObject); |
402 | if (!target || target->isBinding()) |
403 | return scope.engine->throwTypeError(); |
404 | |
405 | ScopedValue boundThis(scope, argc ? argv[0] : Value::undefinedValue()); |
406 | Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr); |
407 | |
408 | int nArgs = (argc - 1 >= 0) ? argc - 1 : 0; |
409 | if (target->isBoundFunction()) { |
410 | BoundFunction *bound = static_cast<BoundFunction *>(target.getPointer()); |
411 | Scoped<MemberData> oldArgs(scope, bound->boundArgs()); |
412 | boundThis = bound->boundThis(); |
413 | int oldSize = !oldArgs ? 0 : oldArgs->size(); |
414 | if (oldSize + nArgs) { |
415 | boundArgs = MemberData::allocate(e: scope.engine, n: oldSize + nArgs); |
416 | boundArgs->d()->values.size = oldSize + nArgs; |
417 | for (uint i = 0; i < static_cast<uint>(oldSize); ++i) |
418 | boundArgs->set(e: scope.engine, index: i, v: oldArgs->data()[i]); |
419 | for (uint i = 0; i < static_cast<uint>(nArgs); ++i) |
420 | boundArgs->set(e: scope.engine, index: oldSize + i, v: argv[i + 1]); |
421 | } |
422 | target = bound->target(); |
423 | } else if (nArgs) { |
424 | boundArgs = MemberData::allocate(e: scope.engine, n: nArgs); |
425 | boundArgs->d()->values.size = nArgs; |
426 | for (uint i = 0, ei = static_cast<uint>(nArgs); i < ei; ++i) |
427 | boundArgs->set(e: scope.engine, index: i, v: argv[i + 1]); |
428 | } |
429 | |
430 | ScopedContext ctx(scope, target->scope()); |
431 | Heap::BoundFunction *bound = BoundFunction::create(scope: ctx, target, boundThis, boundArgs); |
432 | bound->setFunction(target->function()); |
433 | return bound->asReturnedValue(); |
434 | } |
435 | |
436 | ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc) |
437 | { |
438 | if (!argc) |
439 | return Encode(false); |
440 | const Object *o = thisObject->as<Object>(); |
441 | if (!o) |
442 | return Encode(false); |
443 | |
444 | return Object::virtualInstanceOf(typeObject: o, var: argv[0]); |
445 | } |
446 | |
447 | DEFINE_OBJECT_VTABLE(ScriptFunction); |
448 | |
449 | ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget) |
450 | { |
451 | ExecutionEngine *v4 = fo->engine(); |
452 | const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); |
453 | Q_ASSERT(newTarget->isFunctionObject()); |
454 | const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget); |
455 | |
456 | Scope scope(v4); |
457 | Scoped<InternalClass> ic(scope); |
458 | if (nt->d() == f->d()) { |
459 | ic = f->classForConstructor(); |
460 | } else { |
461 | ScopedObject o(scope, nt->protoProperty()); |
462 | ic = scope.engine->internalClasses(icType: EngineBase::Class_Object); |
463 | if (o) |
464 | ic = ic->changePrototype(proto: o->d()); |
465 | } |
466 | ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); |
467 | |
468 | JSTypesStackFrame frame; |
469 | frame.init(v4Function: f->function(), argv, argc); |
470 | frame.setupJSFrame(stackSpace: v4->jsStackTop, function: *f, scope: f->scope(), |
471 | thisObject, |
472 | newTarget: newTarget ? *newTarget : Value::undefinedValue()); |
473 | |
474 | frame.push(engine: v4); |
475 | v4->jsStackTop += frame.requiredJSStackFrameSize(); |
476 | |
477 | ReturnedValue result = Moth::VME::exec(frame: &frame, engine: v4); |
478 | |
479 | frame.pop(engine: v4); |
480 | |
481 | if (Q_UNLIKELY(v4->hasException)) |
482 | return Encode::undefined(); |
483 | else if (!Value::fromReturnedValue(val: result).isObject()) |
484 | return thisObject->asReturnedValue(); |
485 | return result; |
486 | } |
487 | |
488 | DEFINE_OBJECT_VTABLE(ArrowFunction); |
489 | |
490 | void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject *thisObject, |
491 | void **a, const QMetaType *types, int argc) |
492 | { |
493 | if (fo->function()->kind != Function::AotCompiled) { |
494 | QV4::convertAndCall(engine: fo->engine(), thisObject, a, types, argc, |
495 | call: [fo](const Value *thisObject, const Value *argv, int argc) { |
496 | return ArrowFunction::virtualCall(f: fo, thisObject, argv, argc); |
497 | }); |
498 | return; |
499 | } |
500 | |
501 | QV4::Scope scope(fo->engine()); |
502 | QV4::Scoped<ExecutionContext> context(scope, fo->scope()); |
503 | MetaTypesStackFrame frame; |
504 | frame.init(v4Function: fo->function(), thisObject, context, returnAndArgs: a, metaTypes: types, argc); |
505 | frame.push(engine: scope.engine); |
506 | Moth::VME::exec(frame: &frame, engine: scope.engine); |
507 | frame.pop(engine: scope.engine); |
508 | } |
509 | |
510 | static ReturnedValue qfoDoCall(const QV4::FunctionObject *fo, const QV4::Value *thisObject, |
511 | const QV4::Value *argv, int argc) |
512 | { |
513 | ExecutionEngine *engine = fo->engine(); |
514 | JSTypesStackFrame frame; |
515 | frame.init(v4Function: fo->function(), argv, argc, callerCanHandleTailCall: true); |
516 | frame.setupJSFrame(stackSpace: engine->jsStackTop, function: *fo, scope: fo->scope(), |
517 | thisObject: thisObject ? *thisObject : Value::undefinedValue()); |
518 | |
519 | frame.push(engine); |
520 | engine->jsStackTop += frame.requiredJSStackFrameSize(); |
521 | |
522 | ReturnedValue result; |
523 | |
524 | do { |
525 | frame.setPendingTailCall(false); |
526 | result = Moth::VME::exec(frame: &frame, engine); |
527 | frame.setTailCalling(true); |
528 | } while (frame.pendingTailCall()); |
529 | |
530 | frame.pop(engine); |
531 | |
532 | return result; |
533 | } |
534 | |
535 | ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Value *thisObject, |
536 | const QV4::Value *argv, int argc) |
537 | { |
538 | Function *function = fo->function(); |
539 | switch (function->kind) { |
540 | case Function::AotCompiled: |
541 | return QV4::convertAndCall( |
542 | engine: fo->engine(), aotFunction: function->aotCompiledFunction, thisObject, argv, argc, |
543 | call: [fo](QObject *thisObject, void **a, const QMetaType *types, int argc) { |
544 | ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc); |
545 | }); |
546 | case Function::JsTyped: |
547 | return QV4::coerceAndCall( |
548 | engine: fo->engine(), typedFunction: function->aotCompiledFunction, thisObject, argv, argc, |
549 | call: [fo](const Value *thisObject, const Value *argv, int argc) { |
550 | return qfoDoCall(fo, thisObject, argv, argc); |
551 | }); |
552 | default: |
553 | break; |
554 | } |
555 | |
556 | return qfoDoCall(fo, thisObject, argv, argc); |
557 | } |
558 | |
559 | void Heap::ArrowFunction::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) |
560 | { |
561 | FunctionObject::init(); |
562 | this->scope.set(e: scope->engine(), newVal: scope->d()); |
563 | |
564 | setFunction(function); |
565 | Q_ASSERT(function); |
566 | |
567 | Scope s(scope); |
568 | ScopedFunctionObject f(s, this); |
569 | |
570 | ScopedString name(s, n ? n->d() : function->name()); |
571 | if (name) |
572 | f->setName(name); |
573 | |
574 | Q_ASSERT(internalClass && internalClass->verifyIndex(s.engine->id_length()->propertyKey(), Index_Length)); |
575 | setProperty(e: s.engine, index: Index_Length, v: Value::fromInt32(i: int(function->compiledFunction->length))); |
576 | canBeTailCalled = true; |
577 | } |
578 | |
579 | void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) |
580 | { |
581 | ArrowFunction::init(scope, function); |
582 | Q_ASSERT(!function->isArrowFunction()); |
583 | |
584 | Scope s(scope); |
585 | ScopedFunctionObject f(s, this); |
586 | f->createDefaultPrototypeProperty(protoConstructorSlot: Heap::FunctionObject::Index_ProtoConstructor); |
587 | } |
588 | |
589 | Heap::InternalClass *ScriptFunction::classForConstructor() const |
590 | { |
591 | Scope scope(engine()); |
592 | ScopedValue o(scope, protoProperty()); |
593 | if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->heapObject()) |
594 | return d()->cachedClassForConstructor; |
595 | |
596 | Scoped<InternalClass> ic(scope, engine()->internalClasses(icType: EngineBase::Class_Object)); |
597 | ScopedObject p(scope, o); |
598 | if (p) |
599 | ic = ic->changePrototype(proto: p->d()); |
600 | d()->cachedClassForConstructor.set(e: scope.engine, newVal: ic->d()); |
601 | |
602 | return ic->d(); |
603 | } |
604 | |
605 | DEFINE_OBJECT_VTABLE(ConstructorFunction); |
606 | |
607 | ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
608 | { |
609 | const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f); |
610 | if (!c->d()->isDerivedConstructor) |
611 | return ScriptFunction::virtualCallAsConstructor(fo: f, argv, argc, newTarget); |
612 | |
613 | ExecutionEngine *v4 = f->engine(); |
614 | |
615 | JSTypesStackFrame frame; |
616 | frame.init(v4Function: f->function(), argv, argc); |
617 | frame.setupJSFrame(stackSpace: v4->jsStackTop, function: *f, scope: f->scope(), |
618 | thisObject: Value::emptyValue(), |
619 | newTarget: newTarget ? *newTarget : Value::undefinedValue()); |
620 | |
621 | frame.push(engine: v4); |
622 | v4->jsStackTop += frame.requiredJSStackFrameSize(); |
623 | |
624 | ReturnedValue result = Moth::VME::exec(frame: &frame, engine: v4); |
625 | ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); |
626 | |
627 | frame.pop(engine: v4); |
628 | |
629 | if (Q_UNLIKELY(v4->hasException)) |
630 | return Encode::undefined(); |
631 | else if (Value::fromReturnedValue(val: result).isObject()) |
632 | return result; |
633 | else if (!Value::fromReturnedValue(val: result).isUndefined()) |
634 | return v4->throwTypeError(); |
635 | else if (Value::fromReturnedValue(val: thisObject).isEmpty()) { |
636 | Scope scope(v4); |
637 | ScopedString s(scope, v4->newString(QStringLiteral("this" ))); |
638 | return v4->throwReferenceError(value: s); |
639 | } |
640 | return thisObject; |
641 | } |
642 | |
643 | ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
644 | { |
645 | return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|" )); |
646 | } |
647 | |
648 | DEFINE_OBJECT_VTABLE(MemberFunction); |
649 | |
650 | DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); |
651 | |
652 | ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
653 | { |
654 | const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f); |
655 | ExecutionEngine *v4 = f->engine(); |
656 | |
657 | Scope scope(v4); |
658 | |
659 | if (!c->d()->isDerivedConstructor) { |
660 | ScopedObject proto(scope, static_cast<const Object *>(newTarget)->get(name: scope.engine->id_prototype())); |
661 | ScopedObject c(scope, scope.engine->newObject()); |
662 | c->setPrototypeUnchecked(proto); |
663 | return c->asReturnedValue(); |
664 | } |
665 | |
666 | ScopedFunctionObject super(scope, f->getPrototypeOf()); |
667 | Q_ASSERT(super->isFunctionObject()); |
668 | |
669 | JSTypesStackFrame frame; |
670 | frame.init(v4Function: nullptr, argv, argc); |
671 | frame.setupJSFrame(stackSpace: v4->jsStackTop, function: *f, scope: f->scope(), |
672 | thisObject: Value::undefinedValue(), |
673 | newTarget: newTarget ? *newTarget : Value::undefinedValue(), nFormals: argc, nRegisters: argc); |
674 | |
675 | frame.push(engine: v4); |
676 | v4->jsStackTop += frame.requiredJSStackFrameSize(nRegisters: argc); |
677 | |
678 | // Do a super call |
679 | ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); |
680 | ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); |
681 | |
682 | frame.pop(engine: v4); |
683 | |
684 | if (Q_UNLIKELY(v4->hasException)) |
685 | return Encode::undefined(); |
686 | else if (Value::fromReturnedValue(val: result).isObject()) |
687 | return result; |
688 | else if (!Value::fromReturnedValue(val: result).isUndefined()) |
689 | return v4->throwTypeError(); |
690 | else if (Value::fromReturnedValue(val: thisObject).isEmpty()) { |
691 | Scope scope(v4); |
692 | ScopedString s(scope, v4->newString(QStringLiteral("this" ))); |
693 | return v4->throwReferenceError(value: s); |
694 | } |
695 | |
696 | return thisObject; |
697 | } |
698 | |
699 | ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
700 | { |
701 | return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|" )); |
702 | } |
703 | |
704 | DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); |
705 | |
706 | DEFINE_OBJECT_VTABLE(BoundFunction); |
707 | |
708 | void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, |
709 | const Value &boundThis, QV4::MemberData *boundArgs) |
710 | { |
711 | Scope s(scope); |
712 | Heap::FunctionObject::init(scope, QStringLiteral("__bound function__" )); |
713 | this->target.set(e: s.engine, newVal: target->d()); |
714 | this->boundArgs.set(e: s.engine, newVal: boundArgs ? boundArgs->d() : nullptr); |
715 | this->boundThis.set(e: scope->engine(), newVal: boundThis); |
716 | |
717 | if (!target->isConstructor()) |
718 | jsConstruct = nullptr; |
719 | |
720 | ScopedObject f(s, this); |
721 | |
722 | ScopedValue l(s, target->get(name: s.engine->id_length())); |
723 | int len = l->toUInt32(); |
724 | if (boundArgs) |
725 | len -= boundArgs->size(); |
726 | if (len < 0) |
727 | len = 0; |
728 | f->defineReadonlyConfigurableProperty(name: s.engine->id_length(), value: Value::fromInt32(i: len)); |
729 | |
730 | ScopedProperty pd(s); |
731 | pd->value = s.engine->thrower(); |
732 | pd->set = s.engine->thrower(); |
733 | f->insertMember(s: s.engine->id_arguments(), p: pd, attributes: Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); |
734 | f->insertMember(s: s.engine->id_caller(), p: pd, attributes: Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); |
735 | } |
736 | |
737 | ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) |
738 | { |
739 | QV4::ExecutionEngine *v4 = fo->engine(); |
740 | if (v4->hasException) |
741 | return Encode::undefined(); |
742 | |
743 | const BoundFunction *f = static_cast<const BoundFunction *>(fo); |
744 | Scope scope(v4); |
745 | Scoped<MemberData> boundArgs(scope, f->boundArgs()); |
746 | ScopedFunctionObject target(scope, f->target()); |
747 | JSCallArguments jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); |
748 | *jsCallData.thisObject = f->boundThis(); |
749 | Value *argp = jsCallData.args; |
750 | if (boundArgs) { |
751 | memcpy(dest: argp, src: boundArgs->data(), n: boundArgs->size()*sizeof(Value)); |
752 | argp += boundArgs->size(); |
753 | } |
754 | memcpy(dest: argp, src: argv, n: argc*sizeof(Value)); |
755 | return checkedResult(v4, result: target->call(data: jsCallData)); |
756 | } |
757 | |
758 | ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) |
759 | { |
760 | const BoundFunction *f = static_cast<const BoundFunction *>(fo); |
761 | Scope scope(f->engine()); |
762 | |
763 | if (scope.hasException()) |
764 | return Encode::undefined(); |
765 | |
766 | Scoped<MemberData> boundArgs(scope, f->boundArgs()); |
767 | ScopedFunctionObject target(scope, f->target()); |
768 | JSCallArguments jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); |
769 | Value *argp = jsCallData.args; |
770 | if (boundArgs) { |
771 | memcpy(dest: argp, src: boundArgs->data(), n: boundArgs->size()*sizeof(Value)); |
772 | argp += boundArgs->size(); |
773 | } |
774 | memcpy(dest: argp, src: argv, n: argc*sizeof(Value)); |
775 | return target->callAsConstructor(data: jsCallData); |
776 | } |
777 | |