aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-03-12 23:25:30 +0100
committerUlf Hermann <ulf.hermann@qt.io>2021-03-23 06:53:10 +0100
commita5e1a7d70b5eb23af4a5f819dc08b6c15aa723f1 (patch)
tree40ec05f88f96fd085c0add8eb702304dcce8fa61 /src/qml/jsruntime
parent4333dded89ce2e49f9ef3ef50ff474ec04e284dd (diff)
Optimize stack frame setup for AOT compiled functions
When called via the metaobject system, parameters and return values are passed as void*, with accompanying type information in the form of QMetaType. The same format is expected when calling an AOT compiled function. Previously, we would first convert all the parameters to QV4::Value, just to convert them back the moment we notice that there is an AOT compiled function. This is wasteful. This change provides a second call infrastructure that accepts void* and QMetaType as parameter and return value format, and passes them as-is all the way to any AOT compiled functions. If there is no AOT compiled function, the conversion is done when detecting this, rather than when initiating the call. This also passes the information "ignore return value" all the way down to the actual function call. If the caller is not interested in the return value, we don't have to marshal it back at all. For now, we only add the extra "callWithMetaTypes" vtable entry to ArrowFunction. However, other callables could also receive variants optimized for calling with void*/int rather than V4 values. This required changing the way how function arguments are stored in the property cache. We squeeze the return type into QQmlPropertyCacheMethodArguments now, and we use QMetaType instead of integers. In turn, we remove some unused bits. Change-Id: I946e603e623d9d985c54d3a15f6f4b7c7b7d8c60 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4context.cpp10
-rw-r--r--src/qml/jsruntime/qv4context_p.h2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp14
-rw-r--r--src/qml/jsruntime/qv4function.cpp47
-rw-r--r--src/qml/jsruntime/qv4function_p.h2
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp97
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h10
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp22
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4global_p.h2
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h92
-rw-r--r--src/qml/jsruntime/qv4module.cpp10
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp27
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h4
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h219
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp131
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h6
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h65
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 \