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