diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2024-04-30 11:55:01 +0200 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2024-05-14 14:16:57 +0200 |
| commit | 8b6a9403bf2e04d34b9b07d2780186029fab99d0 (patch) | |
| tree | 3849365bf54aea733e6ee3af0165bcb95215c9d2 /src/qml/jsruntime/qv4functionobject.cpp | |
| parent | 2f7a97806e950967ba67b6f07614ca0def8bda48 (diff) | |
V4: Slim down FunctionObject
Most FunctionObjects do not actually need their custom jsCall members.
They will only call the functions from the vtable anyway. FunctionObject
can therefore be split into a static and a dynamic variant. Only the
dyanmic variant needs to carry (and invoke) the extra pointer. The
jsCallWithMetaTypes pointer is completely pointless because none of the
dynamic functions actually implement it.
Furthermore, the QV4::Function and QV4::ExecutionContext pointers in
FunctionObject are only needed by actual JavaScript functions. The
builtins that like to be dynamic functions never need them. Therefore,
split out another class for this.
In the generic FunctionObject, we need the capability to decide at run
time whether the function shall be a constructor or not. Add a flag to
replace the check for jsCallAsConstructor.
Also, where we can, avoid the pessimization of checking whether a
function is a constructor before trying to call it as constructor.
Rather have the default implementation throw the exception.
As a side effect, for most functions we don't need an ExecutionContext
anymore. The engine is enough.
Task-number: QTBUG-124662
Change-Id: Iac657fa71288dd6ec230a33de2986ba3bcf4628c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4functionobject.cpp')
| -rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 237 |
1 files changed, 124 insertions, 113 deletions
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index ab6a34435f..153e8964e8 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -30,84 +30,58 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, - VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes) +void Heap::FunctionObject::init(QV4::ExecutionEngine *engine, QV4::String *name) { - jsCall = call; - jsCallWithMetaTypes = callWithMetaTypes; - jsConstruct = nullptr; - Object::init(); - this->scope.set(scope->engine(), scope->d()); - Scope s(scope->engine()); + Scope s(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; - jsCallWithMetaTypes = vtable()->callWithMetaTypes; - jsConstruct = vtable()->callAsConstructor; - - Object::init(); - this->scope.set(scope->engine(), scope->d()); - Scope s(e); - ScopedFunctionObject f(s, this); - if (name) - f->setName(name); + isConstructor = (vtable()->callAsConstructor != QV4::FunctionObject::virtualCallAsConstructor); } - - -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) +void Heap::FunctionObject::init(QV4::ExecutionEngine *engine, const QString &name) { - jsCall = vtable()->call; - jsCallWithMetaTypes = vtable()->callWithMetaTypes; - 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); + Scope valueScope(engine); + ScopedString s(valueScope, engine->newString(name)); + init(engine, s); } -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name) +void Heap::FunctionObject::init() { - Scope valueScope(scope); - ScopedString s(valueScope, valueScope.engine->newString(name)); - init(scope, s); + init(internalClass->engine, static_cast<QV4::String *>(nullptr)); } -void Heap::FunctionObject::init() +void Heap::JavaScriptFunctionObject::init( + QV4::ExecutionContext *scope, Function *function, QV4::String *n) { - jsCall = vtable()->call; - jsCallWithMetaTypes = vtable()->callWithMetaTypes; - jsConstruct = vtable()->callAsConstructor; - - Object::init(); - this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); + Scope s(scope->engine()); + ScopedString name(s, n ? n->d() : function->name()); + FunctionObject::init(s.engine, name); + this->scope.set(s.engine, scope->d()); + setFunction(function); } -void Heap::FunctionObject::setFunction(Function *f) +void Heap::JavaScriptFunctionObject::setFunction(Function *f) { if (f) { function = f; function->executableCompilationUnit()->addref(); } } -void Heap::FunctionObject::destroy() +void Heap::JavaScriptFunctionObject::destroy() { if (function) function->executableCompilationUnit()->release(); - Object::destroy(); + FunctionObject::destroy(); +} + +void Heap::DynamicFunctionObject::init( + QV4::ExecutionEngine *engine, QV4::String *name, VTable::Call call) +{ + FunctionObject::init(engine, name); + jsCall = call; } void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) @@ -121,32 +95,37 @@ void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) defineDefaultProperty(s.engine->id_prototype(), proto, Attr_NotEnumerable|Attr_NotConfigurable); } -void FunctionObject::call(QObject *thisObject, void **a, const QMetaType *types, int argc) +ReturnedValue FunctionObject::name() const { - if (const auto callWithMetaTypes = d()->jsCallWithMetaTypes) { - callWithMetaTypes(this, thisObject, a, types, argc); - return; - } + return get(engine()->id_name()); +} - QV4::convertAndCall(engine(), thisObject, a, types, argc, - [this](const Value *thisObject, const Value *argv, int argc) { - return call(thisObject, argv, argc); - }); +ReturnedValue FunctionObject::virtualCall( + const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); } -ReturnedValue FunctionObject::name() const +void FunctionObject::virtualCallWithMetaTypes( + const FunctionObject *f, QObject *, void **, const QMetaType *, int) { - return get(scope()->internalClass->engine->id_name()); + f->engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); } -ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) +ReturnedValue FunctionObject::virtualCallAsConstructor( + const FunctionObject *f, const Value *, int, const Value *) { - return Encode::undefined(); + return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); } -void FunctionObject::virtualCallWithMetaTypes( - const FunctionObject *, QObject *, void **, const QMetaType *, int) +void FunctionObject::virtualConvertAndCall( + const FunctionObject *f, QObject *thisObject, + void **argv, const QMetaType *types, int argc) { + QV4::convertAndCall(f->engine(), thisObject, argv, types, argc, + [f](const Value *thisObject, const Value *argv, int argc) { + return f->call(thisObject, argv, argc); + }); } Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) @@ -156,15 +135,20 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); } -Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) +Heap::FunctionObject *FunctionObject::createConstructorFunction( + ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) { + QV4::ExecutionEngine *engine = scope->engine(); if (!function) { - Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); + Heap::DefaultClassConstructorFunction *c + = 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()); + + Heap::ConstructorFunction *c + = engine->memoryManager->allocate<ConstructorFunction>(scope, function); + c->homeObject.set(engine, homeObject->d()); c->isDerivedConstructor = isDerivedConstructor; return c; } @@ -183,7 +167,8 @@ Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *eng if (!name) name = engine->newString(QChar::fromLatin1('[') + QStringView{nameOrSymbol->toQString()}.mid(1) + QChar::fromLatin1(']')); - ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code)); + ScopedFunctionObject function( + scope, engine->memoryManager->allocate<DynamicFunctionObject>(engine, name, code)); function->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(argumentCount)); return function->d(); } @@ -199,16 +184,29 @@ ReturnedValue FunctionObject::getHomeObject() const return Encode::undefined(); } -QQmlSourceLocation FunctionObject::sourceLocation() const +DEFINE_OBJECT_VTABLE(JavaScriptFunctionObject); + +QQmlSourceLocation JavaScriptFunctionObject::sourceLocation() const { return d()->function->sourceLocation(); } +DEFINE_OBJECT_VTABLE(DynamicFunctionObject); + +ReturnedValue DynamicFunctionObject::virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { + Heap::DynamicFunctionObject *d = static_cast<const DynamicFunctionObject *>(f)->d(); + if (d->jsCall) + return d->jsCall(f, thisObject, argv, argc); + return d->internalClass->engine->throwTypeError( + QStringLiteral("Function can only be called with |new|.")); +} + DEFINE_OBJECT_VTABLE(FunctionCtor); -void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) +void Heap::FunctionCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Function")); + Heap::FunctionObject::init(engine, QStringLiteral("Function")); } // 15.3.2 @@ -311,6 +309,12 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); } +ReturnedValue FunctionPrototype::virtualCall( + const FunctionObject *, const Value *, const Value *, int) +{ + return Encode::undefined(); +} + ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); @@ -427,10 +431,7 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu boundArgs->set(scope.engine, i, argv[i + 1]); } - ScopedContext ctx(scope, target->scope()); - Scoped<BoundFunction> bound(scope, BoundFunction::create(ctx, target, boundThis, boundArgs)); - bound->d()->setFunction(target->function()); - return bound->asReturnedValue(); + return BoundFunction::create(target, boundThis, boundArgs)->asReturnedValue(); } ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc) @@ -490,7 +491,9 @@ DEFINE_OBJECT_VTABLE(ArrowFunction); void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject *thisObject, void **a, const QMetaType *types, int argc) { - if (fo->function()->kind != Function::AotCompiled) { + const ArrowFunction *self = static_cast<const ArrowFunction *>(fo); + Function *function = self->function(); + if (function->kind != Function::AotCompiled) { QV4::convertAndCall(fo->engine(), thisObject, a, types, argc, [fo](const Value *thisObject, const Value *argv, int argc) { return ArrowFunction::virtualCall(fo, thisObject, argv, argc); @@ -499,16 +502,17 @@ void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject * } QV4::Scope scope(fo->engine()); - QV4::Scoped<ExecutionContext> context(scope, fo->scope()); + QV4::Scoped<ExecutionContext> context(scope, self->scope()); MetaTypesStackFrame frame; - frame.init(fo->function(), thisObject, context, a, types, argc); + frame.init(function, thisObject, context, a, types, argc); frame.push(scope.engine); Moth::VME::exec(&frame, scope.engine); frame.pop(scope.engine); } -static ReturnedValue qfoDoCall(const QV4::FunctionObject *fo, const QV4::Value *thisObject, - const QV4::Value *argv, int argc) +static ReturnedValue qfoDoCall( + const QV4::JavaScriptFunctionObject *fo, const QV4::Value *thisObject, + const QV4::Value *argv, int argc) { ExecutionEngine *engine = fo->engine(); JSTypesStackFrame frame; @@ -535,7 +539,8 @@ static ReturnedValue qfoDoCall(const QV4::FunctionObject *fo, const QV4::Value * ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Value *thisObject, const QV4::Value *argv, int argc) { - Function *function = fo->function(); + const ArrowFunction *self = static_cast<const ArrowFunction *>(fo); + Function *function = self->function(); switch (function->kind) { case Function::AotCompiled: return QV4::convertAndCall( @@ -546,31 +551,22 @@ ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Va case Function::JsTyped: return QV4::coerceAndCall( fo->engine(), &function->jsTypedFunction, function->compiledFunction, argv, argc, - [fo, thisObject](const Value *argv, int argc) { - return qfoDoCall(fo, thisObject, argv, argc); + [self, thisObject](const Value *argv, int argc) { + return qfoDoCall(self, thisObject, argv, argc); }); default: break; } - return qfoDoCall(fo, thisObject, argv, argc); + return qfoDoCall(self, thisObject, argv, argc); } 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); + JavaScriptFunctionObject::init(scope, function, n); 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; @@ -586,6 +582,13 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_ProtoConstructor); } +void Heap::DefaultClassConstructorFunction::init(QV4::ExecutionContext *scope) +{ + Scope s(scope->engine()); + FunctionObject::init(s.engine, nullptr); + this->scope.set(s.engine, scope->d()); +} + Heap::InternalClass *ScriptFunction::classForConstructor() const { Scope scope(engine()); @@ -613,8 +616,8 @@ ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject ExecutionEngine *v4 = f->engine(); JSTypesStackFrame frame; - frame.init(f->function(), argv, argc); - frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + frame.init(c->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, c->scope(), Value::emptyValue(), newTarget ? *newTarget : Value::undefinedValue()); @@ -668,7 +671,7 @@ ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const Fu JSTypesStackFrame frame; frame.init(nullptr, argv, argc); - frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + frame.setupJSFrame(v4->jsStackTop, *f, c->scope(), Value::undefinedValue(), newTarget ? *newTarget : Value::undefinedValue(), argc, argc); @@ -705,33 +708,41 @@ DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); DEFINE_OBJECT_VTABLE(BoundFunction); -void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, - const Value &boundThis, QV4::MemberData *boundArgs) +void Heap::BoundFunction::init( + QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - Scope s(scope); - Heap::FunctionObject::init(scope, QStringLiteral("__bound function__")); + ExecutionEngine *engine = target->engine(); + Scope s(engine); + ScopedString name(s, engine->newString(QStringLiteral("__bound function__"))); + if (auto *js = target->as<QV4::JavaScriptFunctionObject>()) { + ScopedContext ctx(s, js->scope()); + JavaScriptFunctionObject::init(ctx, js->function(), name); + } else { + JavaScriptFunctionObject::init(engine->rootContext(), nullptr, name); + } + this->target.set(s.engine, target->d()); this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr); - this->boundThis.set(scope->engine(), boundThis); + this->boundThis.set(s.engine, boundThis); if (!target->isConstructor()) - jsConstruct = nullptr; + isConstructor = false; ScopedObject f(s, this); - ScopedValue l(s, target->get(s.engine->id_length())); + ScopedValue l(s, target->get(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)); + f->defineReadonlyConfigurableProperty(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); + pd->value = engine->thrower(); + pd->set = engine->thrower(); + f->insertMember(engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) |
