diff options
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4argumentsobject.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4argumentsobject_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4context_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 14 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 47 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 97 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject_p.h | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject.cpp | 22 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4global_p.h | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 92 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4module.cpp | 10 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 27 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stackframe_p.h | 219 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 131 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth_p.h | 6 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vtable_p.h | 65 |
20 files changed, 576 insertions, 198 deletions
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 206e2b9aa4..46d0b511ca 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -51,7 +51,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); DEFINE_OBJECT_VTABLE(StrictArgumentsObject); -void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) +void Heap::StrictArgumentsObject::init(QV4::JSTypesStackFrame *frame) { Q_ASSERT(vtable() == QV4::StrictArgumentsObject::staticVTable()); @@ -68,11 +68,11 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) Scope scope(v4); Scoped<QV4::StrictArgumentsObject> args(scope, this); - args->arrayReserve(frame->originalArgumentsCount); - args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount); + args->arrayReserve(frame->argc()); + args->arrayPut(0, frame->argv(), frame->argc()); Q_ASSERT(args->internalClass()->verifyIndex(v4->id_length()->propertyKey(), LengthPropertyIndex)); - setProperty(v4, LengthPropertyIndex, Value::fromInt32(frame->originalArgumentsCount)); + setProperty(v4, LengthPropertyIndex, Value::fromInt32(frame->argc())); } void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) @@ -93,7 +93,7 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); fullyCreated = false; - argCount = frame->originalArgumentsCount; + argCount = frame->argc(); uint nFormals = frame->v4Function->nFormals; mapped = nFormals > 63 ? std::numeric_limits<quint64>::max() : (1ull << nFormals) - 1; } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index f0e2192c7e..9e1fe10965 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -84,7 +84,7 @@ DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) { CalleePropertyIndex = 2, CalleeSetterPropertyIndex = 3 }; - void init(CppStackFrame *frame); + void init(JSTypesStackFrame *frame); }; } diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 018571e325..282046f3fa 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -95,12 +95,12 @@ Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine, return c; } -Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) +Heap::CallContext *ExecutionContext::newCallContext(JSTypesStackFrame *frame) { Function *function = frame->v4Function; Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); - uint nFormals = qMax(static_cast<uint>(frame->originalArgumentsCount), function->nFormals); + uint nFormals = qMax(static_cast<uint>(frame->argc()), function->nFormals); uint localsAndFormals = function->compiledFunction->nLocals + nFormals; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); @@ -122,9 +122,9 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->setupLocalTemporalDeadZone(compiledFunction); Value *args = c->locals.values + nLocals; - ::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value)); - c->nArgs = frame->originalArgumentsCount; - for (uint i = frame->originalArgumentsCount; i < function->nFormals; ++i) + ::memcpy(args, frame->argv(), frame->argc() * sizeof(Value)); + c->nArgs = frame->argc(); + for (uint i = frame->argc(); i < function->nFormals; ++i) args[i] = Encode::undefined(); return c; diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 75fa2d08e6..cc27d20fd4 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -152,7 +152,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); static Heap::CallContext *cloneBlockContext(ExecutionEngine *engine, Heap::CallContext *callContext); - static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); + static Heap::CallContext *newCallContext(JSTypesStackFrame *frame); Heap::ExecutionContext *newWithContext(Heap::Object *with) const; static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 182e131c9e..8c4277a682 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1258,13 +1258,15 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const frame.line = qAbs(f->lineNumber()); frame.column = -1; stack.append(frame); - if (f->isTailCalling) { - QV4::StackFrame frame; - frame.function = QStringLiteral("[elided tail calls]"); - stack.append(frame); + if (f->isJSTypesFrame()) { + if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) { + QV4::StackFrame frame; + frame.function = QStringLiteral("[elided tail calls]"); + stack.append(frame); + } } --frameLimit; - f = f->parent; + f = f->parentFrame(); } return stack; @@ -1322,7 +1324,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) base = f->v4Function->finalUrl(); break; } - f = f->parent; + f = f->parentFrame(); } if (base.isEmpty() && globalCode) diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 8164bae549..348837c3a5 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -51,26 +51,53 @@ #include <assembler/MacroAssemblerCodeRef.h> #include <private/qv4vme_moth_p.h> #include <private/qqmlglobal_p.h> +#include <private/qv4jscall_p.h> QT_BEGIN_NAMESPACE using namespace QV4; -ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { +void Function::call(const Value *thisObject, void **a, const QMetaType *types, int argc, + const ExecutionContext *context) +{ + if (!aotFunction) { + QV4::convertAndCall(context->engine(), thisObject, a, types, argc, + [this, context](const Value *thisObject, const Value *argv, int argc) { + return call(thisObject, argv, argc, context); + }); + return; + } + ExecutionEngine *engine = context->engine(); - CppStackFrame frame; - frame.init(engine, this, argv, argc); + MetaTypesStackFrame frame; + frame.init(this, a, types, argc); frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), - thisObject ? *thisObject : Value::undefinedValue(), - Value::undefinedValue()); - - frame.push(); + thisObject ? *thisObject : Value::undefinedValue()); + frame.push(engine); engine->jsStackTop += frame.requiredJSStackFrameSize(); + Moth::VME::exec(&frame, engine); + frame.pop(engine); +} - ReturnedValue result = Moth::VME::exec(&frame, engine); - - frame.pop(); +ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { + if (aotFunction) { + return QV4::convertAndCall( + context->engine(), aotFunction, thisObject, argv, argc, + [this, context](const Value *thisObject, + void **a, const QMetaType *types, int argc) { + call(thisObject, a, types, argc, context); + }); + } + ExecutionEngine *engine = context->engine(); + JSTypesStackFrame frame; + frame.init(this, argv, argc); + frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), + thisObject ? *thisObject : Value::undefinedValue()); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + frame.push(engine); + ReturnedValue result = Moth::VME::exec(&frame, engine); + frame.pop(engine); return result; } diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index c022f84205..a15344dacd 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -100,6 +100,8 @@ public: return compilationUnit->runtimeStrings[i]; } + void call(const Value *thisObject, void **a, const QMetaType *types, int argc, + const ExecutionContext *context); ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); const char *codeData; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 9701e0e9ca..d3bafbe055 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -70,9 +70,11 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call) +void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, + VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes) { jsCall = call; + jsCallWithMetaTypes = callWithMetaTypes; jsConstruct = nullptr; Object::init(); @@ -88,6 +90,7 @@ 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(); @@ -103,6 +106,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name) void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) { jsCall = vtable()->call; + jsCallWithMetaTypes = vtable()->callWithMetaTypes; jsConstruct = vtable()->callAsConstructor; Object::init(); @@ -125,6 +129,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam void Heap::FunctionObject::init() { jsCall = vtable()->call; + jsCallWithMetaTypes = vtable()->callWithMetaTypes; jsConstruct = vtable()->callAsConstructor; Object::init(); @@ -156,6 +161,19 @@ void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) defineDefaultProperty(s.engine->id_prototype(), proto, Attr_NotEnumerable|Attr_NotConfigurable); } +void FunctionObject::call(const Value *thisObject, void **a, const QMetaType *types, int argc) +{ + if (const auto callWithMetaTypes = d()->jsCallWithMetaTypes) { + callWithMetaTypes(this, thisObject, a, types, argc); + return; + } + + QV4::convertAndCall(engine(), thisObject, a, types, argc, + [this](const Value *thisObject, const Value *argv, int argc) { + return call(thisObject, argv, argc); + }); +} + ReturnedValue FunctionObject::name() const { return get(scope()->internalClass->engine->id_name()); @@ -166,6 +184,11 @@ ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, return Encode::undefined(); } +void FunctionObject::virtualCallWithMetaTypes( + const FunctionObject *, const Value *, void **, const QMetaType *, int) +{ +} + Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { if (function->isArrowFunction()) @@ -487,18 +510,18 @@ ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, } ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); - CppStackFrame frame; - frame.init(v4, f->function(), argv, argc); + JSTypesStackFrame frame; + frame.init(f->function(), argv, argc); frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), thisObject, newTarget ? *newTarget : Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result = Moth::VME::exec(&frame, v4); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -509,27 +532,57 @@ ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, DEFINE_OBJECT_VTABLE(ArrowFunction); +void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, const Value *thisObject, + void **a, const QMetaType *types, int argc) +{ + if (!fo->function()->aotFunction) { + QV4::convertAndCall(fo->engine(), thisObject, a, types, argc, + [fo](const Value *thisObject, const Value *argv, int argc) { + return ArrowFunction::virtualCall(fo, thisObject, argv, argc); + }); + return; + } + + ExecutionEngine *engine = fo->engine(); + MetaTypesStackFrame frame; + frame.init(fo->function(), a, types, argc); + frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), + thisObject ? *thisObject : Value::undefinedValue()); + + frame.push(engine); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + Moth::VME::exec(&frame, engine); + frame.pop(engine); +} + ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { + if (const auto *aotFunction = fo->function()->aotFunction) { + return QV4::convertAndCall( + fo->engine(), aotFunction, thisObject, argv, argc, + [fo](const Value *thisObject, void **a, const QMetaType *types, int argc) { + ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc); + }); + } + ExecutionEngine *engine = fo->engine(); - CppStackFrame frame; - frame.init(engine, fo->function(), argv, argc, true); + JSTypesStackFrame frame; + frame.init(fo->function(), argv, argc, true); frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), - thisObject ? *thisObject : Value::undefinedValue(), - Value::undefinedValue()); + thisObject ? *thisObject : Value::undefinedValue()); - frame.push(); + frame.push(engine); engine->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result; do { - frame.pendingTailCall = false; + frame.setPendingTailCall(false); result = Moth::VME::exec(&frame, engine); - frame.isTailCalling = true; - } while (frame.pendingTailCall); + frame.setTailCalling(true); + } while (frame.pendingTailCall()); - frame.pop(); + frame.pop(engine); return result; } @@ -590,19 +643,19 @@ ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject ExecutionEngine *v4 = f->engine(); - CppStackFrame frame; - frame.init(v4, f->function(), argv, argc); + JSTypesStackFrame frame; + frame.init(f->function(), argv, argc); frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), Value::emptyValue(), newTarget ? *newTarget : Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result = Moth::VME::exec(&frame, v4); ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -644,20 +697,20 @@ ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const Fu ScopedFunctionObject super(scope, f->getPrototypeOf()); Q_ASSERT(super->isFunctionObject()); - CppStackFrame frame; - frame.init(v4, nullptr, argv, argc); + JSTypesStackFrame frame; + frame.init(nullptr, argv, argc); frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), Value::undefinedValue(), newTarget ? *newTarget : Value::undefinedValue(), argc, argc); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(argc); // Do a super call ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 78be58c60a..741519389e 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -72,6 +72,7 @@ namespace Heap { Member(class, NoMark, Function *, function) \ Member(class, NoMark, VTable::Call, jsCall) \ Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) \ + Member(class, NoMark, VTable::CallWithMetaTypes, jsCallWithMetaTypes) \ Member(class, NoMark, bool, canBeTailCalled) DECLARE_HEAP_OBJECT(FunctionObject, Object) { @@ -86,7 +87,9 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { return jsConstruct != nullptr; } - Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); + Q_QML_PRIVATE_EXPORT void init( + QV4::ExecutionContext *scope, QV4::String *name, + VTable::Call call, VTable::CallWithMetaTypes callWithMetaTypes = nullptr); Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); @@ -202,6 +205,9 @@ struct Q_QML_EXPORT FunctionObject: Object { return d()->jsCall(this, thisObject, argv, argc); } static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + void call(const Value *thisObject, void **a, const QMetaType *types, int argc); + static void virtualCallWithMetaTypes(const FunctionObject *f, const Value *thisObject, + void **a, const QMetaType *types, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); @@ -277,6 +283,8 @@ struct ArrowFunction : FunctionObject { V4_INTERNALCLASS(ArrowFunction) enum { NInlineProperties = 3 }; + static void virtualCallWithMetaTypes(const FunctionObject *f, const Value *thisObject, + void **a, const QMetaType *types, int argc); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 5077bf1d2b..d13b0e1ce1 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -110,17 +110,17 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu for (int i = 0; i < argc; i++) gp->values->arrayData->setArrayData(engine, i, argv[i]); - gp->cppFrame.init(engine, function, gp->values->arrayData->values.values, argc); + gp->cppFrame.init(function, gp->values->arrayData->values.values, argc); gp->cppFrame.setupJSFrame(gp->jsFrame->arrayData->values.values, *gf, gf->scope(), thisObject ? *thisObject : Value::undefinedValue(), Value::undefinedValue()); - gp->cppFrame.push(); + gp->cppFrame.push(engine); Moth::VME::interpret(&gp->cppFrame, engine, function->codeData); gp->state = GeneratorState::SuspendedStart; - gp->cppFrame.pop(); + gp->cppFrame.pop(engine); return g->asReturnedValue(); } @@ -214,25 +214,25 @@ ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) { Heap::GeneratorObject *gp = d(); gp->state = GeneratorState::Executing; - gp->cppFrame.parent = engine->currentStackFrame; + gp->cppFrame.setParentFrame(engine->currentStackFrame); engine->currentStackFrame = &gp->cppFrame; - Q_ASSERT(gp->cppFrame.yield != nullptr); - const char *code = gp->cppFrame.yield; - gp->cppFrame.yield = nullptr; + Q_ASSERT(gp->cppFrame.yield() != nullptr); + const char *code = gp->cppFrame.yield(); + gp->cppFrame.setYield(nullptr); gp->cppFrame.jsFrame->accumulator = arg; - gp->cppFrame.yieldIsIterator = false; + gp->cppFrame.setYieldIsIterator(false); Scope scope(engine); ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code)); - engine->currentStackFrame = gp->cppFrame.parent; + engine->currentStackFrame = gp->cppFrame.parentFrame(); - bool done = (gp->cppFrame.yield == nullptr); + bool done = (gp->cppFrame.yield() == nullptr); gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield; if (engine->hasException) return Encode::undefined(); - if (gp->cppFrame.yieldIsIterator) + if (gp->cppFrame.yieldIsIterator()) return result->asReturnedValue(); return IteratorPrototype::createIterResultObject(engine, result, done); } diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 10eea5e46b..6eb7b306e0 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -89,7 +89,7 @@ struct GeneratorPrototype : FunctionObject { Member(class, Pointer, ExecutionContext *, context) \ Member(class, Pointer, GeneratorFunction *, function) \ Member(class, NoMark, GeneratorState, state) \ - Member(class, NoMark, CppStackFrame, cppFrame) \ + Member(class, NoMark, JSTypesStackFrame, cppFrame) \ Member(class, Pointer, ArrayObject *, values) \ Member(class, Pointer, ArrayObject *, jsFrame) diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 77f8fe75d0..1493456853 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -146,6 +146,8 @@ namespace Heap { } struct CppStackFrame; +struct JSTypesStackFrame; +struct MetaTypesStackFrame; class MemoryManager; class ExecutableAllocator; struct PropertyKey; diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 15ede20be3..285164604c 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -56,6 +56,7 @@ #include "qv4context_p.h" #include "qv4scopedvalue_p.h" #include "qv4stackframe_p.h" +#include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE @@ -140,19 +141,104 @@ struct ScopedStackFrame { ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context) : scope(scope) { - frame.parent = scope.engine->currentStackFrame; + frame.setParentFrame(scope.engine->currentStackFrame); if (!context) return; frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value))); frame.jsFrame->context = context; - frame.v4Function = frame.parent ? frame.parent->v4Function : nullptr; + if (auto *parent = frame.parentFrame()) + frame.v4Function = parent->v4Function; + else + frame.v4Function = nullptr; scope.engine->currentStackFrame = &frame; } ~ScopedStackFrame() { - scope.engine->currentStackFrame = frame.parent; + scope.engine->currentStackFrame = frame.parentFrame(); } }; +template<typename Callable> +ReturnedValue convertAndCall( + ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFunction *aotFunction, + const Value *thisObject, const Value *argv, int argc, Callable call) +{ + const qsizetype numFunctionArguments = aotFunction->argumentTypes.size(); + Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *)); + Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType)); + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const QMetaType argumentType = aotFunction->argumentTypes[i]; + types[i + 1] = argumentType; + if (const qsizetype argumentSize = argumentType.sizeOf()) { + Q_ALLOCA_VAR(void, argument, argumentSize); + argumentType.construct(argument); + if (i < argc) + engine->metaTypeFromJS(argv[i], argumentType.id(), argument); + values[i + 1] = argument; + } else { + values[i + 1] = nullptr; + } + } + + Q_ALLOCA_DECLARE(void, returnValue); + types[0] = aotFunction->returnType; + if (const qsizetype returnSize = types[0].sizeOf()) { + Q_ALLOCA_ASSIGN(void, returnValue, returnSize); + types[0].construct(returnValue); + values[0] = returnValue; + } else { + values[0] = nullptr; + } + + call(thisObject, values, types, argc); + + ReturnedValue result; + if (values[0]) { + result = engine->metaTypeToJS(types[0], values[0]); + types[0].destruct(values[0]); + } else { + result = Encode::undefined(); + } + + for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i) + types[i].destruct(values[i]); + + return result; +} + +template<typename Callable> +void convertAndCall(ExecutionEngine *engine, const Value *thisObject, + void **a, const QMetaType *types, int argc, Callable call) +{ + Scope scope(engine); + QV4::JSCallArguments jsCallData(scope, argc); + + for (int ii = 0; ii < argc; ++ii) + jsCallData.args[ii] = engine->metaTypeToJS(types[ii + 1], a[ii + 1]); + + void *result = a[0]; + if (!result) { + call(thisObject, jsCallData.args, argc); + return; + } + + ScopedValue jsResult(scope, call(thisObject, jsCallData.args, argc)); + + const QMetaType resultType = types[0]; + if (scope.hasException()) { + // Clear the return value + resultType.destruct(result); + resultType.construct(result, nullptr); + } else { + // When the return type is QVariant, JS objects are to be returned as + // QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately. + if (resultType == QMetaType::fromType<QVariant>()) + *static_cast<QVariant *>(result) = scope.engine->toVariant(jsResult, 0); + else + scope.engine->metaTypeFromJS(jsResult, resultType.id(), result); + } +} + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp index 08a1900383..26bef9cd37 100644 --- a/src/qml/jsruntime/qv4module.cpp +++ b/src/qml/jsruntime/qv4module.cpp @@ -112,15 +112,15 @@ void Module::evaluate() ExecutionEngine *v4 = engine(); Function *moduleFunction = unit->runtimeFunctions[unit->data->indexOfRootFunction]; - CppStackFrame frame; - frame.init(v4, moduleFunction, nullptr, 0); + JSTypesStackFrame frame; + frame.init(moduleFunction, nullptr, 0); frame.setupJSFrame(v4->jsStackTop, Value::undefinedValue(), d()->scope, Value::undefinedValue(), Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); - auto frameCleanup = qScopeGuard([&frame]() { - frame.pop(); + auto frameCleanup = qScopeGuard([&frame, v4]() { + frame.pop(v4); }); Moth::VME::exec(&frame, v4); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 46c6ce4854..a3b1a52535 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1572,7 +1572,7 @@ ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } -ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *engine) +ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine *engine) { // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than // the jitted function, so it can safely do a tail call. @@ -1588,18 +1588,19 @@ ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *eng return engine->throwTypeError(); const FunctionObject &fo = static_cast<const FunctionObject &>(function); - if (!frame->callerCanHandleTailCall || !fo.canBeTailCalled() || engine->debugger() + if (!frame->callerCanHandleTailCall() || !fo.canBeTailCalled() || engine->debugger() || unsigned(argc) > fo.formalParameterCount()) { // Cannot tailcall, do a normal call: return checkedResult(engine, fo.call(&thisObject, argv, argc)); } memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc, - frame->callerCanHandleTailCall); - frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); - engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); - frame->pendingTailCall = true; + frame->init(fo.function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall()); + frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject, + Primitive::undefinedValue()); + engine->jsStackTop = frame->framePointer() + frame->requiredJSStackFrameSize(); + frame->setPendingTailCall(true); return Encode::undefined(); } @@ -1650,7 +1651,7 @@ QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIn return TypeofValue::call(engine, prop); } -void Runtime::PushCallContext::call(CppStackFrame *frame) +void Runtime::PushCallContext::call(JSTypesStackFrame *frame) { frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue(); } @@ -1930,14 +1931,18 @@ QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *e QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); - return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); + return engine->memoryManager->allocObject<StrictArgumentsObject>( + ic, static_cast<JSTypesStackFrame *>(engine->currentStackFrame))->asReturnedValue(); } QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex) { - const Value *values = engine->currentStackFrame->originalArguments + argIndex; - int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + const Value *values = frame->argv() + argIndex; + int nValues = frame->argc() - argIndex; if (nValues <= 0) return engine->newArrayObject(0)->asReturnedValue(); return engine->newArrayObject(values, nValues)->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index d155187e48..012a43251a 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -125,7 +125,7 @@ struct Q_QML_PRIVATE_EXPORT Runtime { }; struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes> { - static ReturnedValue call(CppStackFrame *, ExecutionEngine *); + static ReturnedValue call(JSTypesStackFrame *, ExecutionEngine *engine); }; /* construct */ @@ -235,7 +235,7 @@ struct Q_QML_PRIVATE_EXPORT Runtime { }; struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes> { - static void call(CppStackFrame *); + static void call(JSTypesStackFrame *); }; struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes> { diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index a78aacf509..5423ae35a8 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -55,55 +55,63 @@ #include <private/qv4calldata_p.h> #include <private/qv4function_p.h> +#include <type_traits> + QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_EXPORT CppStackFrame { - EngineBase *engine; +struct CppStackFrame; +struct Q_QML_PRIVATE_EXPORT CppStackFrameBase +{ + enum class Kind : quint8 { Bare, JS, Meta }; + Value *savedStackTop; CppStackFrame *parent; Function *v4Function; CallData *jsFrame; - const Value *originalArguments; int originalArgumentsCount; int instructionPointer; - const char *yield; - const char *unwindHandler; - const char *unwindLabel; - int unwindLevel; - bool yieldIsIterator; - bool callerCanHandleTailCall; - bool pendingTailCall; - bool isTailCalling; - - void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) { - this->engine = engine; + union { + struct { + const Value *originalArguments; + const char *yield; + const char *unwindHandler; + const char *unwindLabel; + int unwindLevel; + bool yieldIsIterator; + bool callerCanHandleTailCall; + bool pendingTailCall; + bool isTailCalling; + }; + struct { + const QMetaType *metaTypes; + void **returnAndArgs; + }; + }; + + Kind kind; +}; + +struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase +{ + // We want to have those public but we can't declare them as public without making the struct + // non-standard layout. So we have this other struct with "using" in between. + using CppStackFrameBase::instructionPointer; + using CppStackFrameBase::v4Function; + using CppStackFrameBase::jsFrame; + + void init(Function *v4Function, int argc, Kind kind = Kind::Bare) { this->v4Function = v4Function; - originalArguments = argv; originalArgumentsCount = argc; instructionPointer = 0; - yield = nullptr; - unwindHandler = nullptr; - unwindLabel = nullptr; - unwindLevel = 0; - yieldIsIterator = false; - this->callerCanHandleTailCall = callerCanHandleTailCall; - pendingTailCall = false; - isTailCalling = false; + this->kind = kind; } - void push() { - parent = engine->currentStackFrame; - engine->currentStackFrame = this; - savedStackTop = engine->jsStackTop; - } - - void pop() { - engine->currentStackFrame = parent; - engine->jsStackTop = savedStackTop; - } + bool isBareStackFrame() const { return kind == Kind::Bare; } + bool isJSTypesFrame() const { return kind == Kind::JS; } + bool isMetaTypesFrame() const { return kind == Kind::Meta; } static uint requiredJSStackFrameSize(uint nRegisters) { return CallData::HeaderSize() + nRegisters; @@ -114,13 +122,9 @@ struct Q_QML_EXPORT CppStackFrame { uint requiredJSStackFrameSize() const { return requiredJSStackFrameSize(v4Function); } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, - const Value &thisObject, const Value &newTarget = Value::undefinedValue()) { - setupJSFrame(stackSpace, function, scope, thisObject, newTarget, - v4Function->compiledFunction->nFormals, v4Function->compiledFunction->nRegisters); - } - void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, - const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) + const Value &thisObject, const Value &newTarget = Value::undefinedValue()) { jsFrame = reinterpret_cast<CallData *>(stackSpace); jsFrame->function = function; @@ -128,22 +132,116 @@ struct Q_QML_EXPORT CppStackFrame { jsFrame->accumulator = Encode::undefined(); jsFrame->thisObject = thisObject; jsFrame->newTarget = newTarget; + } + + QString source() const; + QString function() const; + int lineNumber() const; + ReturnedValue thisObject() const; - if (v4Function && v4Function->aotFunction) - return; + ExecutionContext *context() const + { + return static_cast<ExecutionContext *>(&jsFrame->context); + } + + void setContext(ExecutionContext *context) + { + jsFrame->context = context; + } + + Heap::CallContext *callContext() const + { + Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } + + CppStackFrame *parentFrame() const { return parent; } + void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; } + + int argc() const { return originalArgumentsCount; } + Value *framePointer() const { return savedStackTop; } + + void push(EngineBase *engine) { + parent = engine->currentStackFrame; + engine->currentStackFrame = this; + savedStackTop = engine->jsStackTop; + } + + void pop(EngineBase *engine) { + engine->currentStackFrame = parent; + engine->jsStackTop = savedStackTop; + } +}; + +struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame +{ + void init(Function *v4Function, void **a, const QMetaType *types, int argc) + { + CppStackFrame::init(v4Function, argc, Kind::Meta); + metaTypes = types; + returnAndArgs = a; + } + + QMetaType returnType() const { return metaTypes[0]; } + void *returnValue() const { return returnAndArgs[0]; } + + const QMetaType *argTypes() const { return metaTypes + 1; } + void **argv() const { return returnAndArgs + 1; } +}; + +struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame +{ + // The JIT needs to poke directly into those using offsetof + using CppStackFrame::unwindHandler; + using CppStackFrame::unwindLabel; + using CppStackFrame::unwindLevel; + + void init(Function *v4Function, const Value *argv, int argc, + bool callerCanHandleTailCall = false) + { + CppStackFrame::init(v4Function, argc, Kind::JS); + CppStackFrame::originalArguments = argv; + CppStackFrame::yield = nullptr; + CppStackFrame::unwindHandler = nullptr; + CppStackFrame::yieldIsIterator = false; + CppStackFrame::callerCanHandleTailCall = callerCanHandleTailCall; + CppStackFrame::pendingTailCall = false; + CppStackFrame::isTailCalling = false; + CppStackFrame::unwindLabel = nullptr; + CppStackFrame::unwindLevel = 0; + } + + const Value *argv() const { return originalArguments; } + + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget = Value::undefinedValue()) { + setupJSFrame(stackSpace, function, scope, thisObject, newTarget, + v4Function->compiledFunction->nFormals, + v4Function->compiledFunction->nRegisters); + } + + void setupJSFrame( + Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) + { + CppStackFrame::setupJSFrame(stackSpace, function, scope, thisObject, newTarget); uint argc = uint(originalArgumentsCount); if (argc > nFormals) argc = nFormals; jsFrame->setArgc(argc); - memcpy(jsFrame->args, originalArguments, argc*sizeof(Value)); + memcpy(jsFrame->args, originalArguments, argc * sizeof(Value)); Q_STATIC_ASSERT(Encode::undefined() == 0); - memset(jsFrame->args + argc, 0, (nRegisters - argc)*sizeof(Value)); + memset(jsFrame->args + argc, 0, (nRegisters - argc) * sizeof(Value)); if (v4Function && v4Function->compiledFunction) { - const int firstDeadZoneRegister = v4Function->compiledFunction->firstTemporalDeadZoneRegister; - const int registerDeadZoneSize = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; + const int firstDeadZoneRegister + = v4Function->compiledFunction->firstTemporalDeadZoneRegister; + const int registerDeadZoneSize + = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize; for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v) @@ -151,22 +249,27 @@ struct Q_QML_EXPORT CppStackFrame { } } - QString source() const; - QString function() const; - inline QV4::ExecutionContext *context() const { - return static_cast<ExecutionContext *>(&jsFrame->context); - } - int lineNumber() const; + bool isTailCalling() const { return CppStackFrame::isTailCalling; } + void setTailCalling(bool tailCalling) { CppStackFrame::isTailCalling = tailCalling; } - inline QV4::Heap::CallContext *callContext() const { - Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ - while (ctx->type != Heap::ExecutionContext::Type_CallContext) - ctx = ctx->outer; - return static_cast<Heap::CallContext *>(ctx); - } - ReturnedValue thisObject() const; + bool pendingTailCall() const { return CppStackFrame::pendingTailCall; } + void setPendingTailCall(bool pending) { CppStackFrame::pendingTailCall = pending; } + + const char *yield() const { return CppStackFrame::yield; } + void setYield(const char *yield) { CppStackFrame::yield = yield; } + + bool yieldIsIterator() const { return CppStackFrame::yieldIsIterator; } + void setYieldIsIterator(bool isIter) { CppStackFrame::yieldIsIterator = isIter; } + + bool callerCanHandleTailCall() const { return CppStackFrame::callerCanHandleTailCall; } }; +Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame)); +Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame)); +Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<JSTypesStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<MetaTypesStackFrame>); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 739dc4b7ba..47d7f277e6 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -429,7 +429,79 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) } \ } while (false) -ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) +void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) +{ + qt_v4ResolvePendingBreakpointsHook(); + if (engine->checkStackLimits()) + return; + ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine); + + Function *function = frame->v4Function; + Q_ASSERT(function->aotFunction); + Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), + function->executableCompilationUnit()->fileName(), + function->compiledFunction->location.line, + function->compiledFunction->location.column); + Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling + + const qsizetype numFunctionArguments = function->aotFunction->argumentTypes.size(); + + Q_ALLOCA_DECLARE(void, *transformedArguments); + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const QMetaType argumentType = function->aotFunction->argumentTypes[i]; + if (frame->argc() > i && argumentType == frame->argTypes()[i]) + continue; + + if (transformedArguments == nullptr) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, numFunctionArguments * sizeof(void *)); + memcpy(transformedArguments, frame->argv(), frame->argc() * sizeof(void *)); + } + + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + argumentType.construct(arg); + if (frame->argc() > i) + QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg); + + transformedArguments[i] = arg; + } + + const QMetaType returnType = function->aotFunction->returnType; + Q_ALLOCA_DECLARE(void, transformedResult); + if (frame->returnValue()) { + if (returnType == frame->returnType()) + returnType.destruct(frame->returnValue()); + else + Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); + } + + QQmlPrivate::AOTCompiledContext aotContext; + if (QV4::Heap::QmlContext *qmlContext = engine->qmlContext()) { + QV4::Heap::QQmlContextWrapper *wrapper = qmlContext->qml(); + aotContext.qmlScopeObject = wrapper->scopeObject; + aotContext.qmlContext = wrapper->context->asQQmlContext(); + } + + aotContext.engine = engine->jsEngine(); + aotContext.compilationUnit = function->executableCompilationUnit(); + function->aotFunction->functionPtr( + &aotContext, transformedResult ? transformedResult : frame->returnValue(), + transformedArguments ? transformedArguments : frame->argv()); + + if (transformedResult) { + QMetaType::convert(returnType, transformedResult, + frame->returnType(), frame->returnValue()); + returnType.destruct(transformedResult); + } + + if (transformedArguments) { + for (int i = 0; i < numFunctionArguments; ++i) { + if (i >= frame->argc() || transformedArguments[i] != frame->argv()[i]) + function->aotFunction->argumentTypes[i].destruct(transformedArguments[i]); + } + } +} + +ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine) { qt_v4ResolvePendingBreakpointsHook(); CHECK_STACK_LIMITS(engine); @@ -461,54 +533,9 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) debugger->enteringFunction(); ReturnedValue result; + Q_ASSERT(!function->aotFunction); if (function->jittedCode != nullptr && debugger == nullptr) { result = function->jittedCode(frame, engine); - } else if (function->aotFunction) { - const qsizetype numFunctionArguments = function->aotFunction->argumentTypes.size(); - Q_ALLOCA_DECLARE(void *, argumentPtrs); - - if (numFunctionArguments > 0) { - Q_ALLOCA_ASSIGN(void *, argumentPtrs, numFunctionArguments * sizeof(void *)); - for (qsizetype i = 0; i < numFunctionArguments; ++i) { - const QMetaType argumentType = function->aotFunction->argumentTypes[i]; - if (const qsizetype argumentSize = argumentType.sizeOf()) { - Q_ALLOCA_VAR(void, argument, argumentSize); - argumentType.construct(argument); - if (i < frame->originalArgumentsCount) { - engine->metaTypeFromJS(frame->originalArguments[i], argumentType.id(), - argument); - } - argumentPtrs[i] = argument; - } else { - argumentPtrs[i] = nullptr; - } - } - } - - Q_ALLOCA_DECLARE(void, returnValue); - const QMetaType returnType = function->aotFunction->returnType; - if (const qsizetype returnSize = returnType.sizeOf()) - Q_ALLOCA_ASSIGN(void, returnValue, returnSize); - - QQmlPrivate::AOTCompiledContext aotContext; - if (QV4::Heap::QmlContext *qmlContext = engine->qmlContext()) { - QV4::Heap::QQmlContextWrapper *wrapper = qmlContext->qml(); - aotContext.qmlScopeObject = wrapper->scopeObject; - aotContext.qmlContext = wrapper->context->asQQmlContext(); - } - aotContext.engine = engine->jsEngine(); - aotContext.compilationUnit = function->executableCompilationUnit(); - function->aotFunction->functionPtr(&aotContext, returnValue, argumentPtrs); - - if (returnValue) { - result = engine->metaTypeToJS(returnType, returnValue); - returnType.destruct(returnValue); - } else { - result = Encode::undefined(); - } - - for (qsizetype i = 0; i < numFunctionArguments; ++i) - function->aotFunction->argumentTypes[i].destruct(argumentPtrs[i]); } else { // interpreter result = interpret(frame, engine, function->codeData); @@ -520,7 +547,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) return result; } -QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) +QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *code) { QV4::Function *function = frame->v4Function; QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>(); @@ -720,14 +747,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(StoreSuperProperty) MOTH_BEGIN_INSTR(Yield) - frame->yield = code; - frame->yieldIsIterator = false; + frame->setYield(code); + frame->setYieldIsIterator(false); return acc; MOTH_END_INSTR(Yield) MOTH_BEGIN_INSTR(YieldStar) - frame->yield = code; - frame->yieldIsIterator = true; + frame->setYield(code); + frame->setYieldIsIterator(true); return acc; MOTH_END_INSTR(YieldStar) diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index b3944f5454..1ccb6bb2ed 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -66,8 +66,10 @@ public: QV4::Function *function; const QV4::ExecutionContext *scope; }; - static QV4::ReturnedValue exec(CppStackFrame *frame, ExecutionEngine *engine); - static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); + + static void exec(MetaTypesStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue exec(JSTypesStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); }; } // namespace Moth diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index 588205f046..a71c9da691 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -84,6 +84,7 @@ struct VTable typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var); typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + typedef void (*CallWithMetaTypes)(const FunctionObject *, const Value *, void **, const QMetaType *, int); typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *); @@ -123,11 +124,63 @@ struct VTable Call call; CallAsConstructor callAsConstructor; + CallWithMetaTypes callWithMetaTypes; ResolveLookupGetter resolveLookupGetter; ResolveLookupSetter resolveLookupSetter; }; +template<VTable::CallWithMetaTypes call> +struct VTableCallWithMetaTypesWrapper { constexpr static VTable::CallWithMetaTypes c = call; }; + +template<VTable::Call call> +struct VTableCallWrapper { constexpr static VTable::Call c = call; }; + +template<class Class> +constexpr VTable::CallWithMetaTypes vtableMetaTypesCallEntry() +{ + // If Class overrides virtualCallWithMetaTypes, return that. + // Otherwise, if it overrides virtualCall, return nullptr so that we convert calls. + // Otherwise, just return whatever the base class had. + + // A simple != is not considered constexpr, so we have to jump through some hoops. + if constexpr (!std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualCallWithMetaTypes>>) { + return Class::virtualCallWithMetaTypes; + } + + if constexpr (!std::is_same_v< + VTableCallWrapper<Class::virtualCall>, + VTableCallWrapper<Class::SuperClass::virtualCall>>) { + return nullptr; + } + + return Class::virtualCallWithMetaTypes; +} + +template<class Class> +constexpr VTable::Call vtableJsTypesCallEntry() +{ + // If Class overrides virtualCall, return that. + // Otherwise, if it overrides virtualCallWithMetaTypes, return nullptr so that we convert calls. + // Otherwise, just return whatever the base class had. + + // A simple != is not considered constexpr, so we have to jump through some hoops. + if constexpr (!std::is_same_v< + VTableCallWrapper<Class::virtualCall>, + VTableCallWrapper<Class::SuperClass::virtualCall>>) { + return Class::virtualCall; + } + + if constexpr (!std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualCallWithMetaTypes>>) { + return nullptr; + } + + return Class::virtualCall; +} struct VTableBase { protected: @@ -150,9 +203,16 @@ protected: static constexpr VTable::Call virtualCall = nullptr; static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; + static constexpr VTable::CallWithMetaTypes virtualCallWithMetaTypes = nullptr; static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr; static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr; + + template<class Class> + friend constexpr VTable::CallWithMetaTypes vtableMetaTypesCallEntry(); + + template<class Class> + friend constexpr VTable::Call vtableJsTypesCallEntry(); }; #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ @@ -190,8 +250,9 @@ protected: classname::virtualOwnPropertyKeys, \ classname::virtualInstanceOf, \ \ - classname::virtualCall, \ - classname::virtualCallAsConstructor, \ + QV4::vtableJsTypesCallEntry<classname>(), \ + classname::virtualCallAsConstructor, \ + QV4::vtableMetaTypesCallEntry<classname>(), \ \ classname::virtualResolveLookupGetter, \ classname::virtualResolveLookupSetter \ |