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