| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQml module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qv4object_p.h" |
| #include "qv4objectproto_p.h" |
| #include "qv4stringobject_p.h" |
| #include "qv4function_p.h" |
| #include "qv4symbol_p.h" |
| #include <private/qv4mm_p.h> |
| |
| #include "qv4arrayobject_p.h" |
| #include "qv4scopedvalue_p.h" |
| #include "qv4argumentsobject_p.h" |
| |
| #include <private/qqmljsengine_p.h> |
| #include <private/qqmljslexer_p.h> |
| #include <private/qqmljsparser_p.h> |
| #include <private/qqmljsast_p.h> |
| #include <private/qqmljavascriptexpression_p.h> |
| #include <private/qqmlengine_p.h> |
| #include <qv4runtimecodegen_p.h> |
| #include "private/qlocale_tools_p.h" |
| #include "private/qqmlbuiltinfunctions_p.h" |
| #include <private/qv4jscall_p.h> |
| #include <private/qv4vme_moth_p.h> |
| #include <private/qv4alloca_p.h> |
| |
| #include <QtCore/QDebug> |
| #include <algorithm> |
| #include "qv4profiling_p.h" |
| |
| using namespace QV4; |
| |
| |
| DEFINE_OBJECT_VTABLE(FunctionObject); |
| |
| void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call) |
| { |
| jsCall = call; |
| jsConstruct = nullptr; |
| |
| Object::init(); |
| this->scope.set(scope->engine(), scope->d()); |
| Scope s(scope->engine()); |
| ScopedFunctionObject f(s, this); |
| if (name) |
| f->setName(name); |
| } |
| |
| void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name) |
| { |
| ExecutionEngine *e = scope->engine(); |
| |
| jsCall = vtable()->call; |
| jsConstruct = vtable()->callAsConstructor; |
| |
| Object::init(); |
| this->scope.set(scope->engine(), scope->d()); |
| Scope s(e); |
| ScopedFunctionObject f(s, this); |
| if (name) |
| f->setName(name); |
| } |
| |
| |
| |
| void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) |
| { |
| jsCall = vtable()->call; |
| jsConstruct = vtable()->callAsConstructor; |
| |
| Object::init(); |
| setFunction(function); |
| this->scope.set(scope->engine(), scope->d()); |
| Scope s(scope->engine()); |
| ScopedString name(s, n ? n->d() : function->name()); |
| ScopedFunctionObject f(s, this); |
| if (name) |
| f->setName(name); |
| } |
| |
| void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name) |
| { |
| Scope valueScope(scope); |
| ScopedString s(valueScope, valueScope.engine->newString(name)); |
| init(scope, s); |
| } |
| |
| void Heap::FunctionObject::init() |
| { |
| jsCall = vtable()->call; |
| jsConstruct = vtable()->callAsConstructor; |
| |
| Object::init(); |
| this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); |
| } |
| |
| void Heap::FunctionObject::setFunction(Function *f) |
| { |
| if (f) { |
| function = f; |
| function->executableCompilationUnit()->addref(); |
| } |
| } |
| void Heap::FunctionObject::destroy() |
| { |
| if (function) |
| function->executableCompilationUnit()->release(); |
| Object::destroy(); |
| } |
| |
| void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) |
| { |
| Scope s(this); |
| |
| Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->verifyIndex(s.engine->id_constructor()->propertyKey(), protoConstructorSlot)); |
| |
| ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto))); |
| proto->setProperty(protoConstructorSlot, d()); |
| defineDefaultProperty(s.engine->id_prototype(), proto, Attr_NotEnumerable|Attr_NotConfigurable); |
| } |
| |
| ReturnedValue FunctionObject::name() const |
| { |
| return get(scope()->internalClass->engine->id_name()); |
| } |
| |
| ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) |
| { |
| return Encode::undefined(); |
| } |
| |
| Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) |
| { |
| if (function->isArrowFunction()) |
| return scope->engine()->memoryManager->allocate<ArrowFunction>(scope, function); |
| return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); |
| } |
| |
| Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) |
| { |
| if (!function) { |
| Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); |
| c->isDerivedConstructor = isDerivedConstructor; |
| return c; |
| } |
| Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); |
| c->homeObject.set(scope->engine(), homeObject->d()); |
| c->isDerivedConstructor = isDerivedConstructor; |
| return c; |
| } |
| |
| Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, QV4::String *name) |
| { |
| Heap::MemberFunction *m = scope->engine()->memoryManager->allocate<MemberFunction>(scope, function, name); |
| m->homeObject.set(scope->engine(), homeObject->d()); |
| return m; |
| } |
| |
| Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount) |
| { |
| Scope scope(engine); |
| ScopedString name(scope, nameOrSymbol); |
| if (!name) |
| name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']')); |
| |
| ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code)); |
| function->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(argumentCount)); |
| return function->d(); |
| } |
| |
| ReturnedValue FunctionObject::getHomeObject() const |
| { |
| const MemberFunction *m = as<MemberFunction>(); |
| if (m) |
| return m->d()->homeObject->asReturnedValue(); |
| const ConstructorFunction *c = as<ConstructorFunction>(); |
| if (c) |
| return c->d()->homeObject->asReturnedValue(); |
| return Encode::undefined(); |
| } |
| |
| QQmlSourceLocation FunctionObject::sourceLocation() const |
| { |
| return d()->function->sourceLocation(); |
| } |
| |
| DEFINE_OBJECT_VTABLE(FunctionCtor); |
| |
| void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) |
| { |
| Heap::FunctionObject::init(scope, QStringLiteral("Function")); |
| } |
| |
| // 15.3.2 |
| QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) |
| { |
| QString arguments; |
| QString body; |
| if (argc > 0) { |
| for (int i = 0, ei = argc - 1; i < ei; ++i) { |
| if (i) |
| arguments += QLatin1String(", "); |
| arguments += argv[i].toQString(); |
| } |
| body = argv[argc - 1].toQString(); |
| } |
| if (engine->hasException) |
| return nullptr; |
| |
| QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}"); |
| |
| QQmlJS::Engine ee; |
| QQmlJS::Lexer lexer(&ee); |
| lexer.setCode(function, 1, false); |
| QQmlJS::Parser parser(&ee); |
| |
| const bool parsed = parser.parseExpression(); |
| |
| if (!parsed) { |
| engine->throwSyntaxError(QLatin1String("Parse error")); |
| return nullptr; |
| } |
| |
| QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); |
| if (!fe) { |
| engine->throwSyntaxError(QLatin1String("Parse error")); |
| return nullptr; |
| } |
| |
| Compiler::Module module(engine->debugger() != nullptr); |
| |
| Compiler::JSUnitGenerator jsGenerator(&module); |
| RuntimeCodegen cg(engine, &jsGenerator, false); |
| cg.generateFromFunctionExpression(QString(), function, fe, &module); |
| |
| if (engine->hasException) |
| return nullptr; |
| |
| return ExecutableCompilationUnit::create(cg.generateCompilationUnit()); |
| } |
| |
| ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
| { |
| ExecutionEngine *engine = f->engine(); |
| |
| QQmlRefPointer<ExecutableCompilationUnit> compilationUnit |
| = parse(engine, argv, argc, Type_Function); |
| if (engine->hasException) |
| return Encode::undefined(); |
| |
| Function *vmf = compilationUnit->linkToEngine(engine); |
| ExecutionContext *global = engine->scriptContext(); |
| ReturnedValue o = Encode(FunctionObject::createScriptFunction(global, vmf)); |
| |
| if (!newTarget) |
| return o; |
| Scope scope(engine); |
| ScopedObject obj(scope, o); |
| obj->setProtoFromNewTarget(newTarget); |
| return obj->asReturnedValue(); |
| } |
| |
| // 15.3.1: This is equivalent to new Function(...) |
| ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| return virtualCallAsConstructor(f, argv, argc, f); |
| } |
| |
| DEFINE_OBJECT_VTABLE(FunctionPrototype); |
| |
| void Heap::FunctionPrototype::init() |
| { |
| Heap::FunctionObject::init(); |
| } |
| |
| void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) |
| { |
| Scope scope(engine); |
| ScopedObject o(scope); |
| |
| ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1)); |
| ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); |
| |
| defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty()); |
| defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0)); |
| defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); |
| defineDefaultProperty(engine->id_toString(), method_toString, 0); |
| defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); |
| defineDefaultProperty(QStringLiteral("call"), method_call, 1); |
| defineDefaultProperty(QStringLiteral("bind"), method_bind, 1); |
| defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); |
| } |
| |
| ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
| { |
| ExecutionEngine *v4 = b->engine(); |
| const FunctionObject *fun = thisObject->as<FunctionObject>(); |
| if (!fun) |
| return v4->throwTypeError(); |
| |
| const Scope scope(fun->engine()); |
| const ScopedString scopedFunctionName(scope, fun->name()); |
| const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString()); |
| QString functionAsString = QStringLiteral("function"); |
| |
| // If fun->name() is empty, then there is no function name |
| // to append because the function is anonymous. |
| if (!functionName.isEmpty()) |
| functionAsString.append(QLatin1Char(' ') + functionName); |
| |
| functionAsString.append(QStringLiteral("() { [native code] }")); |
| |
| return Encode(v4->newString(functionAsString)); |
| } |
| |
| ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| ExecutionEngine *v4 = b->engine(); |
| const FunctionObject *f = thisObject->as<FunctionObject>(); |
| if (!f) |
| return v4->throwTypeError(); |
| thisObject = argc ? argv : nullptr; |
| if (argc < 2 || argv[1].isNullOrUndefined()) |
| return f->call(thisObject, argv, 0); |
| |
| Object *arr = argv[1].objectValue(); |
| if (!arr) |
| return v4->throwTypeError(); |
| |
| const qint64 len64 = arr->getLength(); |
| if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) |
| return v4->throwRangeError(QStringLiteral("Invalid array length.")); |
| if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop)) |
| return v4->throwRangeError(QStringLiteral("Array too large for apply().")); |
| |
| const uint len = uint(len64); |
| |
| Scope scope(v4); |
| Value *arguments = scope.alloc<Scope::Uninitialized>(len); |
| if (len) { |
| if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { |
| QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); |
| int l = qMin(len, (uint)a->d()->context->argc()); |
| memcpy(arguments, a->d()->context->args(), l*sizeof(Value)); |
| for (quint32 i = l; i < len; ++i) |
| arguments[i] = Value::undefinedValue(); |
| } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { |
| auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); |
| uint alen = sad ? sad->values.size : 0; |
| if (alen > len) |
| alen = len; |
| for (uint i = 0; i < alen; ++i) |
| arguments[i] = sad->data(i); |
| for (quint32 i = alen; i < len; ++i) |
| arguments[i] = Value::undefinedValue(); |
| } else { |
| // need to init the arguments array, as the get() calls below can have side effects |
| memset(arguments, 0, len*sizeof(Value)); |
| for (quint32 i = 0; i < len; ++i) |
| arguments[i] = arr->get(i); |
| } |
| } |
| |
| return f->call(thisObject, arguments, len); |
| } |
| |
| ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| if (!thisObject->isFunctionObject()) |
| return b->engine()->throwTypeError(); |
| |
| const FunctionObject *f = static_cast<const FunctionObject *>(thisObject); |
| |
| thisObject = argc ? argv : nullptr; |
| if (argc) { |
| ++argv; |
| --argc; |
| } |
| return f->call(thisObject, argv, argc); |
| } |
| |
| ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| QV4::Scope scope(b); |
| ScopedFunctionObject target(scope, thisObject); |
| if (!target || target->isBinding()) |
| return scope.engine->throwTypeError(); |
| |
| ScopedValue boundThis(scope, argc ? argv[0] : Value::undefinedValue()); |
| Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr); |
| |
| int nArgs = (argc - 1 >= 0) ? argc - 1 : 0; |
| if (target->isBoundFunction()) { |
| BoundFunction *bound = static_cast<BoundFunction *>(target.getPointer()); |
| Scoped<MemberData> oldArgs(scope, bound->boundArgs()); |
| boundThis = bound->boundThis(); |
| int oldSize = !oldArgs ? 0 : oldArgs->size(); |
| if (oldSize + nArgs) { |
| boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs); |
| boundArgs->d()->values.size = oldSize + nArgs; |
| for (uint i = 0; i < static_cast<uint>(oldSize); ++i) |
| boundArgs->set(scope.engine, i, oldArgs->data()[i]); |
| for (uint i = 0; i < static_cast<uint>(nArgs); ++i) |
| boundArgs->set(scope.engine, oldSize + i, argv[i + 1]); |
| } |
| target = bound->target(); |
| } else if (nArgs) { |
| boundArgs = MemberData::allocate(scope.engine, nArgs); |
| boundArgs->d()->values.size = nArgs; |
| for (uint i = 0, ei = static_cast<uint>(nArgs); i < ei; ++i) |
| boundArgs->set(scope.engine, i, argv[i + 1]); |
| } |
| |
| ScopedContext ctx(scope, target->scope()); |
| Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs); |
| bound->setFunction(target->function()); |
| return bound->asReturnedValue(); |
| } |
| |
| ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc) |
| { |
| if (!argc) |
| return Encode(false); |
| const Object *o = thisObject->as<Object>(); |
| if (!o) |
| return Encode(false); |
| |
| return Object::virtualInstanceOf(o, argv[0]); |
| } |
| |
| DEFINE_OBJECT_VTABLE(ScriptFunction); |
| |
| ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget) |
| { |
| ExecutionEngine *v4 = fo->engine(); |
| const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); |
| Q_ASSERT(newTarget->isFunctionObject()); |
| const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget); |
| |
| Scope scope(v4); |
| Scoped<InternalClass> ic(scope); |
| if (nt->d() == f->d()) { |
| ic = f->classForConstructor(); |
| } else { |
| ScopedObject o(scope, nt->protoProperty()); |
| ic = scope.engine->internalClasses(EngineBase::Class_Object); |
| if (o) |
| ic = ic->changePrototype(o->d()); |
| } |
| ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); |
| |
| CppStackFrame frame; |
| frame.init(v4, f->function(), argv, argc); |
| frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), |
| thisObject, |
| newTarget ? *newTarget : Value::undefinedValue()); |
| |
| frame.push(); |
| v4->jsStackTop += frame.requiredJSStackFrameSize(); |
| |
| ReturnedValue result = Moth::VME::exec(&frame, v4); |
| |
| frame.pop(); |
| |
| if (Q_UNLIKELY(v4->hasException)) |
| return Encode::undefined(); |
| else if (!Value::fromReturnedValue(result).isObject()) |
| return thisObject->asReturnedValue(); |
| return result; |
| } |
| |
| DEFINE_OBJECT_VTABLE(ArrowFunction); |
| |
| ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) |
| { |
| ExecutionEngine *engine = fo->engine(); |
| CppStackFrame frame; |
| frame.init(engine, fo->function(), argv, argc, true); |
| frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), |
| thisObject ? *thisObject : Value::undefinedValue(), |
| Value::undefinedValue()); |
| |
| frame.push(); |
| engine->jsStackTop += frame.requiredJSStackFrameSize(); |
| |
| ReturnedValue result; |
| |
| do { |
| frame.pendingTailCall = false; |
| result = Moth::VME::exec(&frame, engine); |
| frame.isTailCalling = true; |
| } while (frame.pendingTailCall); |
| |
| frame.pop(); |
| |
| return result; |
| } |
| |
| void Heap::ArrowFunction::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) |
| { |
| FunctionObject::init(); |
| this->scope.set(scope->engine(), scope->d()); |
| |
| setFunction(function); |
| Q_ASSERT(function); |
| |
| Scope s(scope); |
| ScopedFunctionObject f(s, this); |
| |
| ScopedString name(s, n ? n->d() : function->name()); |
| if (name) |
| f->setName(name); |
| |
| Q_ASSERT(internalClass && internalClass->verifyIndex(s.engine->id_length()->propertyKey(), Index_Length)); |
| setProperty(s.engine, Index_Length, Value::fromInt32(int(function->compiledFunction->length))); |
| canBeTailCalled = true; |
| } |
| |
| void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) |
| { |
| ArrowFunction::init(scope, function); |
| Q_ASSERT(!function->isArrowFunction()); |
| |
| Scope s(scope); |
| ScopedFunctionObject f(s, this); |
| f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_ProtoConstructor); |
| } |
| |
| Heap::InternalClass *ScriptFunction::classForConstructor() const |
| { |
| Scope scope(engine()); |
| ScopedValue o(scope, protoProperty()); |
| if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->heapObject()) |
| return d()->cachedClassForConstructor; |
| |
| Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object)); |
| ScopedObject p(scope, o); |
| if (p) |
| ic = ic->changePrototype(p->d()); |
| d()->cachedClassForConstructor.set(scope.engine, ic->d()); |
| |
| return ic->d(); |
| } |
| |
| DEFINE_OBJECT_VTABLE(ConstructorFunction); |
| |
| ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
| { |
| const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f); |
| if (!c->d()->isDerivedConstructor) |
| return ScriptFunction::virtualCallAsConstructor(f, argv, argc, newTarget); |
| |
| ExecutionEngine *v4 = f->engine(); |
| |
| CppStackFrame frame; |
| frame.init(v4, f->function(), argv, argc); |
| frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), |
| Value::emptyValue(), |
| newTarget ? *newTarget : Value::undefinedValue()); |
| |
| frame.push(); |
| v4->jsStackTop += frame.requiredJSStackFrameSize(); |
| |
| ReturnedValue result = Moth::VME::exec(&frame, v4); |
| ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); |
| |
| frame.pop(); |
| |
| if (Q_UNLIKELY(v4->hasException)) |
| return Encode::undefined(); |
| else if (Value::fromReturnedValue(result).isObject()) |
| return result; |
| else if (!Value::fromReturnedValue(result).isUndefined()) |
| return v4->throwTypeError(); |
| else if (Value::fromReturnedValue(thisObject).isEmpty()) { |
| Scope scope(v4); |
| ScopedString s(scope, v4->newString(QStringLiteral("this"))); |
| return v4->throwReferenceError(s); |
| } |
| return thisObject; |
| } |
| |
| ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
| { |
| return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); |
| } |
| |
| DEFINE_OBJECT_VTABLE(MemberFunction); |
| |
| DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); |
| |
| ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
| { |
| const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f); |
| ExecutionEngine *v4 = f->engine(); |
| |
| Scope scope(v4); |
| |
| if (!c->d()->isDerivedConstructor) { |
| ScopedObject proto(scope, static_cast<const Object *>(newTarget)->get(scope.engine->id_prototype())); |
| ScopedObject c(scope, scope.engine->newObject()); |
| c->setPrototypeUnchecked(proto); |
| return c->asReturnedValue(); |
| } |
| |
| ScopedFunctionObject super(scope, f->getPrototypeOf()); |
| Q_ASSERT(super->isFunctionObject()); |
| |
| CppStackFrame frame; |
| frame.init(v4, nullptr, argv, argc); |
| frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), |
| Value::undefinedValue(), |
| newTarget ? *newTarget : Value::undefinedValue(), argc, argc); |
| |
| frame.push(); |
| v4->jsStackTop += frame.requiredJSStackFrameSize(argc); |
| |
| // Do a super call |
| ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); |
| ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); |
| |
| frame.pop(); |
| |
| if (Q_UNLIKELY(v4->hasException)) |
| return Encode::undefined(); |
| else if (Value::fromReturnedValue(result).isObject()) |
| return result; |
| else if (!Value::fromReturnedValue(result).isUndefined()) |
| return v4->throwTypeError(); |
| else if (Value::fromReturnedValue(thisObject).isEmpty()) { |
| Scope scope(v4); |
| ScopedString s(scope, v4->newString(QStringLiteral("this"))); |
| return v4->throwReferenceError(s); |
| } |
| |
| return thisObject; |
| } |
| |
| ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
| { |
| return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); |
| } |
| |
| DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); |
| |
| DEFINE_OBJECT_VTABLE(BoundFunction); |
| |
| void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, |
| const Value &boundThis, QV4::MemberData *boundArgs) |
| { |
| Scope s(scope); |
| Heap::FunctionObject::init(scope, QStringLiteral("__bound function__")); |
| this->target.set(s.engine, target->d()); |
| this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr); |
| this->boundThis.set(scope->engine(), boundThis); |
| |
| if (!target->isConstructor()) |
| jsConstruct = nullptr; |
| |
| ScopedObject f(s, this); |
| |
| ScopedValue l(s, target->get(s.engine->id_length())); |
| int len = l->toUInt32(); |
| if (boundArgs) |
| len -= boundArgs->size(); |
| if (len < 0) |
| len = 0; |
| f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(len)); |
| |
| ScopedProperty pd(s); |
| pd->value = s.engine->thrower(); |
| pd->set = s.engine->thrower(); |
| f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); |
| f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); |
| } |
| |
| ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) |
| { |
| const BoundFunction *f = static_cast<const BoundFunction *>(fo); |
| Scope scope(f->engine()); |
| |
| if (scope.hasException()) |
| return Encode::undefined(); |
| |
| Scoped<MemberData> boundArgs(scope, f->boundArgs()); |
| ScopedFunctionObject target(scope, f->target()); |
| JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); |
| *jsCallData->thisObject = f->boundThis(); |
| Value *argp = jsCallData->args; |
| if (boundArgs) { |
| memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); |
| argp += boundArgs->size(); |
| } |
| memcpy(argp, argv, argc*sizeof(Value)); |
| return target->call(jsCallData); |
| } |
| |
| ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) |
| { |
| const BoundFunction *f = static_cast<const BoundFunction *>(fo); |
| Scope scope(f->engine()); |
| |
| if (scope.hasException()) |
| return Encode::undefined(); |
| |
| Scoped<MemberData> boundArgs(scope, f->boundArgs()); |
| ScopedFunctionObject target(scope, f->target()); |
| JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); |
| Value *argp = jsCallData->args; |
| if (boundArgs) { |
| memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); |
| argp += boundArgs->size(); |
| } |
| memcpy(argp, argv, argc*sizeof(Value)); |
| return target->callAsConstructor(jsCallData); |
| } |