aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-05-17 16:38:25 +0200
committerUlf Hermann <ulf.hermann@qt.io>2021-06-10 11:53:19 +0200
commite20650e0702259b4be79be85a3d27e45db42efc1 (patch)
tree7536cee98f4cc502774f807d1a609940cd20d4f5 /src/qml/jsruntime
parent7fa28f98824a94396106eadfc028b329985a0cfc (diff)
Eliminate JS call frame from metatypes calls
If we call an AOT-compiled function we never need the JavaScript call frame. We can just skip its setup and save some overhead. Change-Id: I39dc2ca6eea5b5a66f3b87b642a310534cecf6cd Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/qv4context.cpp9
-rw-r--r--src/qml/jsruntime/qv4engine.cpp5
-rw-r--r--src/qml/jsruntime/qv4engine_p.h5
-rw-r--r--src/qml/jsruntime/qv4function.cpp14
-rw-r--r--src/qml/jsruntime/qv4function_p.h7
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp23
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h53
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp127
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp13
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h157
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp2
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h3
14 files changed, 277 insertions, 150 deletions
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 282046f3fa..fb002ec48d 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -71,8 +71,13 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b
Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
c->outer.set(v4, outer);
- c->function.set(v4, static_cast<Heap::FunctionObject *>(
- Value::fromStaticValue(frame->jsFrame->function).m()));
+ if (frame->isJSTypesFrame()) {
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(
+ Value::fromStaticValue(
+ static_cast<JSTypesStackFrame *>(frame)->jsFrame->function).m()));
+ } else {
+ c->function.set(v4, nullptr);
+ }
c->locals.size = nLocals;
c->locals.alloc = nLocals;
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index fb5f5802f8..6b4fc81bf3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -2112,12 +2112,11 @@ void ExecutionEngine::callInContext(Function *function, QObject *self,
QV4::Scope scope(this);
ExecutionContext *ctx = currentStackFrame ? currentContext() : scriptContext();
QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(ctx, ctxtdata, self));
- QV4::ScopedValue selfValue(scope, QV4::QObjectWrapper::wrap(this, self));
if (!args) {
Q_ASSERT(argc == 0);
void *dummyArgs[] = { nullptr };
QMetaType dummyTypes[] = { QMetaType::fromType<void>() };
- function->call(selfValue, dummyArgs, dummyTypes, argc, qmlContext);
+ function->call(self, dummyArgs, dummyTypes, argc, qmlContext);
return;
}
@@ -2125,7 +2124,7 @@ void ExecutionEngine::callInContext(Function *function, QObject *self,
return;
// implicitly sets the return value, which is args[0]
- function->call(selfValue, args, types, argc, qmlContext);
+ function->call(self, args, types, argc, qmlContext);
}
void ExecutionEngine::initQmlGlobalObject()
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 3120261a94..be115cb064 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -561,10 +561,7 @@ public:
void setProfiler(Profiling::Profiler *profiler);
#endif // QT_CONFIG(qml_debug)
- ExecutionContext *currentContext() const
- {
- return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
- }
+ ExecutionContext *currentContext() const { return currentStackFrame->context(); }
// ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast
quintptr newProtoId() { return (protoIdCount += 2); }
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index db767085da..7b5685a2a0 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -57,8 +57,8 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-bool Function::call(const Value *thisObject, void **a, const QMetaType *types, int argc,
- const ExecutionContext *context)
+bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc,
+ ExecutionContext *context)
{
if (!aotFunction) {
return QV4::convertAndCall(
@@ -70,21 +70,19 @@ bool Function::call(const Value *thisObject, void **a, const QMetaType *types, i
ExecutionEngine *engine = context->engine();
MetaTypesStackFrame frame;
- frame.init(this, a, types, argc);
- frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(),
- thisObject ? *thisObject : Value::undefinedValue());
+ frame.init(this, thisObject, context, a, types, argc);
frame.push(engine);
- engine->jsStackTop += frame.requiredJSStackFrameSize();
Moth::VME::exec(&frame, engine);
frame.pop(engine);
return true;
}
-ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
+ReturnedValue Function::call(
+ const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) {
if (aotFunction) {
return QV4::convertAndCall(
context->engine(), aotFunction, thisObject, argv, argc,
- [this, context](const Value *thisObject,
+ [this, context](QObject *thisObject,
void **a, const QMetaType *types, int argc) {
call(thisObject, a, types, argc, context);
});
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 7577161c01..45f2a6263a 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -100,9 +100,10 @@ public:
return compilationUnit->runtimeStrings[i];
}
- bool 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);
+ bool call(QObject *thisObject, void **a, const QMetaType *types, int argc,
+ ExecutionContext *context);
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc,
+ ExecutionContext *context);
const char *codeData;
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index d3bafbe055..779d975cba 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -161,7 +161,7 @@ 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)
+void FunctionObject::call(QObject *thisObject, void **a, const QMetaType *types, int argc)
{
if (const auto callWithMetaTypes = d()->jsCallWithMetaTypes) {
callWithMetaTypes(this, thisObject, a, types, argc);
@@ -185,7 +185,7 @@ ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *,
}
void FunctionObject::virtualCallWithMetaTypes(
- const FunctionObject *, const Value *, void **, const QMetaType *, int)
+ const FunctionObject *, QObject *, void **, const QMetaType *, int)
{
}
@@ -532,7 +532,7 @@ ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo,
DEFINE_OBJECT_VTABLE(ArrowFunction);
-void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, const Value *thisObject,
+void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject *thisObject,
void **a, const QMetaType *types, int argc)
{
if (!fo->function()->aotFunction) {
@@ -543,16 +543,13 @@ void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, const Val
return;
}
- ExecutionEngine *engine = fo->engine();
+ QV4::Scope scope(fo->engine());
+ QV4::Scoped<ExecutionContext> context(scope, fo->scope());
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);
+ frame.init(fo->function(), thisObject, context, a, types, argc);
+ frame.push(scope.engine);
+ Moth::VME::exec(&frame, scope.engine);
+ frame.pop(scope.engine);
}
ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
@@ -560,7 +557,7 @@ ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *
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) {
+ [fo](QObject *thisObject, void **a, const QMetaType *types, int argc) {
ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc);
});
}
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 741519389e..f831b569b9 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -205,8 +205,8 @@ 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 call(QObject *thisObject, void **a, const QMetaType *types, int argc);
+ static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject,
void **a, const QMetaType *types, int argc);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function);
@@ -283,7 +283,7 @@ struct ArrowFunction : FunctionObject {
V4_INTERNALCLASS(ArrowFunction)
enum { NInlineProperties = 3 };
- static void virtualCallWithMetaTypes(const FunctionObject *f, const Value *thisObject,
+ static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *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 d13b0e1ce1..0319e263e1 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -104,7 +104,8 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu
// We need to set up a separate JSFrame for the generator, as it's being re-entered
Heap::GeneratorObject *gp = g->d();
gp->values.set(engine, engine->newArrayObject(argc));
- gp->jsFrame.set(engine, engine->newArrayObject(CppStackFrame::requiredJSStackFrameSize(function)));
+ gp->jsFrame.set(engine, engine->newArrayObject(
+ JSTypesStackFrame::requiredJSStackFrameSize(function)));
// copy original arguments
for (int i = 0; i < argc; i++)
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c6d320ac20..337b86dbc0 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 "qv4qobjectwrapper_p.h"
#include <private/qv4alloca_p.h>
QT_BEGIN_NAMESPACE
@@ -134,27 +135,24 @@ ReturnedValue FunctionObject::call(const JSCallData &data) const
void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc,
void **args, const QMetaType *types);
-struct ScopedStackFrame {
- Scope &scope;
- CppStackFrame frame;
-
- ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context)
- : scope(scope)
+struct ScopedStackFrame
+{
+ ScopedStackFrame(const Scope &scope, ExecutionContext *context)
+ : engine(scope.engine)
{
- frame.setParentFrame(scope.engine->currentStackFrame);
- if (!context)
- return;
- frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value)));
- frame.jsFrame->context = context;
- if (auto *parent = frame.parentFrame())
- frame.v4Function = parent->v4Function;
- else
- frame.v4Function = nullptr;
- scope.engine->currentStackFrame = &frame;
+ frame.init(engine->currentStackFrame ? engine->currentStackFrame->v4Function : nullptr,
+ nullptr, context, nullptr, nullptr, 0);
+ frame.push(engine);
}
- ~ScopedStackFrame() {
- scope.engine->currentStackFrame = frame.parentFrame();
+
+ ~ScopedStackFrame()
+ {
+ frame.pop(engine);
}
+
+private:
+ ExecutionEngine *engine = nullptr;
+ MetaTypesStackFrame frame;
};
template<typename Callable>
@@ -189,7 +187,10 @@ ReturnedValue convertAndCall(
values[0] = nullptr;
}
- call(thisObject, values, types, argc);
+ if (const QV4::QObjectWrapper *cppThisObject = thisObject->as<QV4::QObjectWrapper>())
+ call(cppThisObject->object(), values, types, argc);
+ else
+ call(nullptr, values, types, argc);
ReturnedValue result;
if (values[0]) {
@@ -206,7 +207,7 @@ ReturnedValue convertAndCall(
}
template<typename Callable>
-bool convertAndCall(ExecutionEngine *engine, const Value *thisObject,
+bool convertAndCall(ExecutionEngine *engine, QObject *thisObject,
void **a, const QMetaType *types, int argc, Callable call)
{
Scope scope(engine);
@@ -215,7 +216,17 @@ bool convertAndCall(ExecutionEngine *engine, const Value *thisObject,
for (int ii = 0; ii < argc; ++ii)
jsCallData.args[ii] = engine->metaTypeToJS(types[ii + 1], a[ii + 1]);
- ScopedValue jsResult(scope, call(thisObject, jsCallData.args, argc));
+ ScopedObject jsThisObject(scope);
+ if (thisObject) {
+ // The result of wrap() can only be null, undefined, or an object.
+ jsThisObject = QV4::QObjectWrapper::wrap(engine, thisObject);
+ if (!jsThisObject)
+ jsThisObject = engine->globalObject;
+ } else {
+ jsThisObject = engine->globalObject;
+ }
+
+ ScopedValue jsResult(scope, call(jsThisObject, jsCallData.args, argc));
void *result = a[0];
if (!result)
return !jsResult->isUndefined();
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 01a7878dae..a168b0e580 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -320,7 +320,7 @@ ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId)
QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit()
->runtimeFunctions[functionId];
Q_ASSERT(clos);
- ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ ExecutionContext *current = engine->currentContext();
if (clos->isGenerator())
return GeneratorFunction::create(current, clos)->asReturnedValue();
return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
@@ -355,7 +355,7 @@ Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name);
+ return engine->currentContext()->deleteProperty(name);
}
ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name)
@@ -1001,7 +1001,7 @@ void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, cons
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value);
+ ExecutionContext::Error e = engine->currentContext()->setProperty(name, value);
if (e == ExecutionContext::RangeError)
engine->globalObject->put(name, value);
@@ -1011,7 +1011,7 @@ void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, cons
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value);
+ ExecutionContext::Error e = engine->currentContext()->setProperty(name, value);
if (e == ExecutionContext::TypeError)
engine->throwTypeError();
else if (e == ExecutionContext::RangeError)
@@ -1042,21 +1042,38 @@ ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name);
+ return engine->currentContext()->getProperty(name);
}
static Object *getSuperBase(Scope &scope)
{
- if (scope.engine->currentStackFrame->jsFrame->thisObject.isEmpty()) {
- scope.engine->throwReferenceError(QStringLiteral("Missing call to super()."), QString(), 0, 0);
- return nullptr;
+ ScopedFunctionObject f(scope);
+ ScopedObject homeObject(scope);
+ if (scope.engine->currentStackFrame->isJSTypesFrame()) {
+ JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+
+ if (frame->jsFrame->thisObject.isEmpty()) {
+ scope.engine->throwReferenceError(
+ QStringLiteral("Missing call to super()."), QString(), 0, 0);
+ return nullptr;
+ }
+
+ f = Value::fromStaticValue(frame->jsFrame->function);
+ homeObject = f->getHomeObject();
+ } else {
+ Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
+ MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+ if (frame->thisObject() == nullptr) {
+ scope.engine->throwReferenceError(
+ QStringLiteral("Missing call to super()."), QString(), 0, 0);
+ return nullptr;
+ }
}
- ScopedFunctionObject f(
- scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function));
- ScopedObject homeObject(scope, f->getHomeObject());
if (!homeObject) {
- ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context));
+ ScopedContext ctx(scope, scope.engine->currentContext());
Q_ASSERT(ctx);
while (ctx) {
if (CallContext *c = ctx->asCallContext()) {
@@ -1067,7 +1084,8 @@ static Object *getSuperBase(Scope &scope)
}
ctx = ctx->d()->outer;
}
- homeObject = f->getHomeObject();
+ if (f)
+ homeObject = f->getHomeObject();
}
if (!homeObject) {
scope.engine->throwTypeError();
@@ -1091,8 +1109,18 @@ ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Va
ScopedPropertyKey key(scope, property.toPropertyKey(engine));
if (engine->hasException)
return Encode::undefined();
- return base->get(
- key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>()));
+
+ if (scope.engine->currentStackFrame->isJSTypesFrame()) {
+ JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+ return base->get(key, &(frame->jsFrame->thisObject.asValue<Value>()));
+ } else {
+ Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
+ MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+ Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject()));
+ return base->get(key, wrapper);
+ }
}
void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value)
@@ -1104,8 +1132,20 @@ void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &pro
ScopedPropertyKey key(scope, property.toPropertyKey(engine));
if (engine->hasException)
return;
- bool result = base->put(
- key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>()));
+
+ bool result;
+ if (scope.engine->currentStackFrame->isJSTypesFrame()) {
+ JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+ result = base->put(key, value, &(frame->jsFrame->thisObject.asValue<Value>()));
+ } else {
+ Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame());
+ MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(
+ scope.engine->currentStackFrame);
+ Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject()));
+ result = base->put(key, value, wrapper);
+ }
+
if (!result && engine->currentStackFrame->v4Function->isStrict())
engine->throwTypeError();
}
@@ -1145,9 +1185,23 @@ void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, c
ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t)
{
- if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) {
- return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number
+ if (engine->currentStackFrame->isJSTypesFrame()) {
+ JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame);
+ if (frame->thisObject() != Value::emptyValue().asReturnedValue()) {
+ // ### TODO: fix line number
+ return engine->throwReferenceError(
+ QStringLiteral("super() already called."), QString(), 0, 0);
+ }
+ } else {
+ Q_ASSERT(engine->currentStackFrame->isMetaTypesFrame());
+ MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(engine->currentStackFrame);
+ if (frame->thisObject() != nullptr) {
+ // ### TODO: fix line number
+ return engine->throwReferenceError(
+ QStringLiteral("super() already called."), QString(), 0, 0);
+ }
}
+
const FunctionObject *f = t.as<FunctionObject>();
if (!f)
return engine->throwTypeError();
@@ -1386,8 +1440,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val
Scope scope(engine);
ScopedValue thisObject(scope);
- ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
- ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject));
+ ScopedFunctionObject function(
+ scope, engine->currentContext()->getPropertyAndBase(engine->id_eval(), thisObject));
if (engine->hasException)
return Encode::undefined();
@@ -1406,8 +1460,7 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va
ScopedValue thisObject(scope);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
- ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject));
+ ScopedFunctionObject f(scope, engine->currentContext()->getPropertyAndBase(name, thisObject));
if (engine->hasException)
return Encode::undefined();
@@ -1659,7 +1712,7 @@ QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIn
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name));
+ ScopedValue prop(scope, engine->currentContext()->getProperty(name));
// typeof doesn't throw. clear any possible exception
scope.engine->hasException = false;
return TypeofValue::call(engine, prop);
@@ -1672,7 +1725,8 @@ void Runtime::PushCallContext::call(JSTypesStackFrame *frame)
ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc)
{
- CallData *jsFrame = engine->currentStackFrame->jsFrame;
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
+ CallData *jsFrame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame;
Value &newAcc = jsFrame->accumulator.asValue<Value>();
newAcc = Value::fromHeapObject(acc.toObject(engine));
if (!engine->hasException) {
@@ -1687,18 +1741,23 @@ ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Valu
void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex)
{
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex];
- engine->currentStackFrame->jsFrame->context = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue();
+ static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context
+ = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue();
}
void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index)
{
- engine->currentStackFrame->jsFrame->context = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
+ static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context
+ = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
}
void Runtime::CloneBlockContext::call(ExecutionEngine *engine)
{
- auto frame = engine->currentStackFrame;
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
+ auto frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame);
auto context = static_cast<Heap::CallContext *>(
Value::fromStaticValue(frame->jsFrame->context).m());
frame->jsFrame->context =
@@ -1707,18 +1766,20 @@ void Runtime::CloneBlockContext::call(ExecutionEngine *engine)
void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index)
{
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
engine->setScriptContext(c);
- engine->currentStackFrame->jsFrame->context = c;
+ static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = c;
}
void Runtime::PopScriptContext::call(ExecutionEngine *engine)
{
+ Q_ASSERT(engine->currentStackFrame->isJSTypesFrame());
ReturnedValue root = engine->rootContext()->asReturnedValue();
engine->setScriptContext(root);
- engine->currentStackFrame->jsFrame->context = root;
+ static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = root;
}
void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex)
@@ -1750,7 +1811,7 @@ void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int name
{
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable);
+ engine->currentContext()->createMutableBinding(name, deletable);
}
ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length)
@@ -1803,7 +1864,7 @@ ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId,
arg = ObjectLiteralArgument::Value;
fnName = name->asFunctionName(engine, prefix);
- ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ ExecutionContext *current = engine->currentContext();
if (clos->isGenerator())
value = MemberGeneratorFunction::create(current, clos, o, fnName)->asReturnedValue();
else
@@ -1861,7 +1922,7 @@ ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex
ScopedObject proto(scope, engine->newObject());
proto->setPrototypeUnchecked(protoParent);
- ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ ExecutionContext *current = engine->currentContext();
ScopedFunctionObject constructor(scope);
QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr;
diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp
index a716c53aea..e99dda591f 100644
--- a/src/qml/jsruntime/qv4stackframe.cpp
+++ b/src/qml/jsruntime/qv4stackframe.cpp
@@ -36,7 +36,9 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
#include "qv4stackframe_p.h"
+#include <private/qv4qobjectwrapper_p.h>
#include <QtCore/qstring.h>
using namespace QV4;
@@ -68,7 +70,12 @@ int CppStackFrame::lineNumber() const
return line->line;
}
-ReturnedValue CppStackFrame::thisObject() const {
- return jsFrame->thisObject.asReturnedValue();
-}
+ReturnedValue QV4::CppStackFrame::thisObject() const
+{
+ if (isJSTypesFrame())
+ return static_cast<const JSTypesStackFrame *>(this)->thisObject();
+ Q_ASSERT(isMetaTypesFrame());
+ const auto metaTypesFrame = static_cast<const MetaTypesStackFrame *>(this);
+ return QObjectWrapper::wrap(metaTypesFrame->context()->engine(), metaTypesFrame->thisObject());
+}
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index ebe3b762af..1439e9f677 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -64,17 +64,17 @@ namespace QV4 {
struct CppStackFrame;
struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
{
- enum class Kind : quint8 { Bare, JS, Meta };
+ enum class Kind : quint8 { JS, Meta };
- Value *savedStackTop;
CppStackFrame *parent;
Function *v4Function;
- CallData *jsFrame;
int originalArgumentsCount;
int instructionPointer;
union {
struct {
+ Value *savedStackTop;
+ CallData *jsFrame;
const Value *originalArguments;
const char *yield;
const char *unwindHandler;
@@ -86,6 +86,8 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrameBase
bool isTailCalling;
};
struct {
+ ExecutionContext *context;
+ QObject *thisObject;
const QMetaType *metaTypes;
void **returnAndArgs;
};
@@ -100,88 +102,67 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase
// 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) {
+ void init(Function *v4Function, int argc, Kind kind) {
this->v4Function = v4Function;
originalArgumentsCount = argc;
instructionPointer = 0;
this->kind = kind;
}
- 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;
- }
- static uint requiredJSStackFrameSize(Function *v4Function) {
- return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
- }
- 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())
- {
- jsFrame = reinterpret_cast<CallData *>(stackSpace);
- jsFrame->function = function;
- jsFrame->context = scope->asReturnedValue();
- jsFrame->accumulator = Encode::undefined();
- jsFrame->thisObject = thisObject;
- jsFrame->newTarget = newTarget;
- }
-
QString source() const;
QString function() const;
int lineNumber() const;
+
+ CppStackFrame *parentFrame() const { return parent; }
+ void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; }
+
+ int argc() const { return originalArgumentsCount; }
+
+ inline ExecutionContext *context() const;
+
+ Heap::CallContext *callContext() const { return callContext(context()->d()); }
ReturnedValue thisObject() const;
- ExecutionContext *context() const
+protected:
+ CppStackFrame() = default;
+
+ void push(EngineBase *engine)
{
- return static_cast<ExecutionContext *>(&jsFrame->context);
+ Q_ASSERT(kind == Kind::JS || kind == Kind::Meta);
+ parent = engine->currentStackFrame;
+ engine->currentStackFrame = this;
}
- void setContext(ExecutionContext *context)
+ void pop(EngineBase *engine)
{
- jsFrame->context = context;
+ engine->currentStackFrame = parent;
}
- Heap::CallContext *callContext() const
+ Heap::CallContext *callContext(Heap::ExecutionContext *ctx) 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)
+ using CppStackFrame::push;
+ using CppStackFrame::pop;
+
+ void init(Function *v4Function, QObject *thisObject, ExecutionContext *context,
+ void **returnAndArgs, const QMetaType *metaTypes, int argc)
{
CppStackFrame::init(v4Function, argc, Kind::Meta);
- metaTypes = types;
- returnAndArgs = a;
+ CppStackFrameBase::thisObject = thisObject;
+ CppStackFrameBase::context = context;
+ CppStackFrameBase::metaTypes = metaTypes;
+ CppStackFrameBase::returnAndArgs = returnAndArgs;
}
QMetaType returnType() const { return metaTypes[0]; }
@@ -189,10 +170,22 @@ struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
const QMetaType *argTypes() const { return metaTypes + 1; }
void **argv() const { return returnAndArgs + 1; }
+
+ QObject *thisObject() const { return CppStackFrameBase::thisObject; }
+
+ ExecutionContext *context() const { return CppStackFrameBase::context; }
+ void setContext(ExecutionContext *context) { CppStackFrameBase::context = context; }
+
+ Heap::CallContext *callContext() const
+ {
+ return CppStackFrame::callContext(CppStackFrameBase::context->d());
+ }
};
struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
{
+ using CppStackFrame::jsFrame;
+
// The JIT needs to poke directly into those using offsetof
using CppStackFrame::unwindHandler;
using CppStackFrame::unwindLabel;
@@ -215,6 +208,16 @@ struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
const Value *argv() const { return originalArguments; }
+ static uint requiredJSStackFrameSize(uint nRegisters) {
+ return CallData::HeaderSize() + nRegisters;
+ }
+ static uint requiredJSStackFrameSize(Function *v4Function) {
+ return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
+ }
+ 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,
@@ -226,7 +229,12 @@ struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
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);
+ jsFrame = reinterpret_cast<CallData *>(stackSpace);
+ jsFrame->function = function;
+ jsFrame->context = scope->asReturnedValue();
+ jsFrame->accumulator = Encode::undefined();
+ jsFrame->thisObject = thisObject;
+ jsFrame->newTarget = newTarget;
uint argc = uint(originalArgumentsCount);
if (argc > nFormals)
@@ -251,6 +259,21 @@ struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
}
}
+ ExecutionContext *context() const
+ {
+ return static_cast<ExecutionContext *>(&jsFrame->context);
+ }
+
+ void setContext(ExecutionContext *context)
+ {
+ jsFrame->context = context;
+ }
+
+ Heap::CallContext *callContext() const
+ {
+ return CppStackFrame::callContext(static_cast<ExecutionContext &>(jsFrame->context).d());
+ }
+
bool isTailCalling() const { return CppStackFrame::isTailCalling; }
void setTailCalling(bool tailCalling) { CppStackFrame::isTailCalling = tailCalling; }
@@ -264,8 +287,34 @@ struct Q_QML_PRIVATE_EXPORT JSTypesStackFrame : public CppStackFrame
void setYieldIsIterator(bool isIter) { CppStackFrame::yieldIsIterator = isIter; }
bool callerCanHandleTailCall() const { return CppStackFrame::callerCanHandleTailCall; }
+
+ ReturnedValue thisObject() const
+ {
+ return jsFrame->thisObject.asReturnedValue();
+ }
+
+ Value *framePointer() const { return savedStackTop; }
+
+ void push(EngineBase *engine) {
+ CppStackFrame::push(engine);
+ savedStackTop = engine->jsStackTop;
+ }
+
+ void pop(EngineBase *engine) {
+ CppStackFrame::pop(engine);
+ engine->jsStackTop = savedStackTop;
+ }
};
+inline ExecutionContext *CppStackFrame::context() const
+{
+ if (isJSTypesFrame())
+ return static_cast<const JSTypesStackFrame *>(this)->context();
+
+ Q_ASSERT(isMetaTypesFrame());
+ return static_cast<const MetaTypesStackFrame *>(this)->context();
+}
+
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame));
Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame));
Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 88bb309b7d..009cff067a 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -343,7 +343,7 @@ static struct InstrCount {
}
#endif
-static inline QV4::Value &stackValue(QV4::Value *stack, size_t slot, const CppStackFrame *frame)
+static inline QV4::Value &stackValue(QV4::Value *stack, size_t slot, const JSTypesStackFrame *frame)
{
Q_ASSERT(slot < CallData::HeaderSize() / sizeof(QV4::StaticValue)
+ frame->jsFrame->argc()
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index a71c9da691..ba44baae1a 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -54,6 +54,7 @@
QT_BEGIN_NAMESPACE
+class QObject;
namespace QV4 {
struct Lookup;
@@ -84,7 +85,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 void (*CallWithMetaTypes)(const FunctionObject *, QObject *, void **, const QMetaType *, int);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);