From 3daf05ec2d7dbc408e2c302cdade667ec719d847 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 30 Oct 2017 10:38:55 +0100 Subject: Introduce new calling convention for builtin functions And implement Function.apply()/call() with it. Change-Id: I028c82d5f9adfd23328d669db1adccec9de5824c Reviewed-by: Erik Verbruggen --- src/qml/jsruntime/qv4functionobject.cpp | 110 +++++++++++++++++--------------- src/qml/jsruntime/qv4functionobject_p.h | 10 ++- src/qml/jsruntime/qv4object.cpp | 21 ++++++ src/qml/jsruntime/qv4object_p.h | 4 ++ 4 files changed, 91 insertions(+), 54 deletions(-) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 1f86672a62..56b62bcad2 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -70,6 +70,20 @@ DEFINE_OBJECT_VTABLE(FunctionObject); Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == Heap::Object::markTable); +void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, + ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)) +{ + jsCall = code; + jsConstruct = QV4::FunctionObject::callAsConstructor; + + Object::init(); + function = nullptr; + this->scope.set(scope->engine(), scope->d()); + Scope s(scope->engine()); + ScopedFunctionObject f(s, this); + f->init(name, false); +} + void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { jsCall = reinterpret_cast(vtable())->call; @@ -273,70 +287,62 @@ ReturnedValue FunctionPrototype::method_toString(const BuiltinFunction *b, CallD return Encode(v4->newString(QStringLiteral("function() { [code] }"))); } -ReturnedValue FunctionPrototype::method_apply(const BuiltinFunction *b, CallData *callData) +ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = b->engine(); - callData->function = callData->thisObject; - FunctionObject *o = callData->function.as(); - if (!o) + const FunctionObject *f = thisObject->as(); + if (!f) return v4->throwTypeError(); - callData->thisObject = callData->argument(0); - callData->accumulator = callData->argument(1); - - if (callData->accumulator.isObject()) { - Object *arr = static_cast(&callData->accumulator); - uint len = arr->getLength(); - callData->setArgc(len); - - v4->jsStackTop = callData->args + len; - if (len) { - if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast()->fullyCreated()) { - QV4::ArgumentsObject *a = arr->cast(); - int l = qMin(len, (uint)a->d()->context->argc()); - memcpy(callData->args, a->d()->context->args(), l*sizeof(Value)); - for (quint32 i = l; i < len; ++i) - callData->args[i] = Primitive::undefinedValue(); - } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { - auto sad = static_cast(arr->arrayData()); - uint alen = sad ? sad->values.size : 0; - if (alen > len) - alen = len; - for (uint i = 0; i < alen; ++i) - callData->args[i] = sad->data(i); - for (quint32 i = alen; i < len; ++i) - callData->args[i] = Primitive::undefinedValue(); - } else { - for (quint32 i = 0; i < len; ++i) - callData->args[i] = arr->getIndexed(i); - } - } - } else if (!callData->accumulator.isNullOrUndefined()) { + 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(); - } else { - callData->setArgc(0); - v4->jsStackTop = callData->args; + + uint len = arr->getLength(); + + Scope scope(v4); + Value *arguments = v4->jsAlloca(len); + if (len) { + if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast()->fullyCreated()) { + QV4::ArgumentsObject *a = arr->cast(); + 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] = Primitive::undefinedValue(); + } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { + auto sad = static_cast(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] = Primitive::undefinedValue(); + } else { + for (quint32 i = 0; i < len; ++i) + arguments[i] = arr->getIndexed(i); + } } - return o->call(&callData->thisObject, callData->args, callData->argc()); + return f->call(thisObject, arguments, len); } -ReturnedValue FunctionPrototype::method_call(const BuiltinFunction *b, CallData *callData) +ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *engine = b->engine(); - if (!callData->thisObject.isFunctionObject()) - return engine->throwTypeError(); + if (!thisObject->isFunctionObject()) + return b->engine()->throwTypeError(); - Q_ASSERT(engine->jsStackTop == callData->args + callData->argc()); + const FunctionObject *f = static_cast(thisObject); - callData->function = callData->thisObject; - callData->thisObject = callData->argc() ? callData->args[0] : Primitive::undefinedValue(); - if (callData->argc()) { - callData->setArgc(callData->argc() - 1); - for (int i = 0, ei = callData->argc(); i < ei; ++i) - callData->args[i] = callData->args[i + 1]; - --engine->jsStackTop; + thisObject = argc ? argv : nullptr; + if (argc) { + ++argv; + --argc; } - return static_cast(callData->function).call(&callData->thisObject, callData->args, callData->argc()); + return f->call(thisObject, argv, argc); } ReturnedValue FunctionPrototype::method_bind(const BuiltinFunction *b, CallData *callData) diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 3fd0fbeec8..86137e8e6e 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -84,6 +84,7 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { Index_ProtoConstructor = 0 }; + void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)); void init(QV4::ExecutionContext *scope, QV4::String *name = 0, bool createProto = false); void init(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false); void init(QV4::ExecutionContext *scope, const QString &name, bool createProto = false); @@ -197,8 +198,8 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); - static ReturnedValue method_apply(const BuiltinFunction *, CallData *callData); - static ReturnedValue method_call(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_bind(const BuiltinFunction *, CallData *callData); }; @@ -211,6 +212,11 @@ struct Q_QML_EXPORT BuiltinFunction : FunctionObject { return scope->engine()->memoryManager->allocObject(scope, name, code); } + static Heap::FunctionObject *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)) + { + return scope->engine()->memoryManager->allocObject(scope, name, code); + } + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 1859466348..bfc4eb9ce1 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -170,6 +170,27 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const Bui defineDefaultProperty(name, function); } +void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + ExecutionContext *global = e->rootContext(); + ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(s, function); +} + +void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +{ + ExecutionEngine *e = engine(); + Scope scope(e); + ExecutionContext *global = e->rootContext(); + ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + defineDefaultProperty(name, function); +} + void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), ReturnedValue (*setter)(const BuiltinFunction *, CallData *)) { diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index af68f09700..d2e7f3ff8c 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -288,8 +288,12 @@ struct Q_QML_EXPORT Object: Managed { insertMember(name, value, Attr_Data|Attr_NotEnumerable); } void defineDefaultProperty(const QString &name, const Value &value); + // old calling convention void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); void defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); + // new calling convention + void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); + void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), ReturnedValue (*setter)(const BuiltinFunction *, CallData *)); void defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), -- cgit v1.2.3