aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2017-11-21 12:29:40 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2017-11-21 12:29:40 +0100
commitd373d5e7d70e968cfba8957596ed6fe4f46990c8 (patch)
treec52bf2b0fbbfdb13d644b4050aa7a931ef4b7109
parent9880acb424fd814501ba5fc4ae1caa989e23fafa (diff)
parent9af8a47746b69b6040fc149c1d24602a1e25b08f (diff)
Merge remote-tracking branch 'origin/wip/new-backend' into dev
Conflicts: src/qml/compiler/qv4isel_moth.cpp src/qml/compiler/qv4jsir_p.h src/qml/jsruntime/qv4engine_p.h src/qml/jsruntime/qv4vme_moth.cpp tests/auto/qml/qml.pro Change-Id: Ia7b6ec24c7fcbcbb1786d9e798d2df294020ae37
-rw-r--r--src/3rdparty/masm/assembler/MacroAssembler.h2
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM64.h9
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARMv7.h11
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86_64.h13
-rw-r--r--src/imports/localstorage/plugin.cpp71
-rw-r--r--src/particles/qquickv4particledata.cpp68
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp69
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h5
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp29
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp17
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp50
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp155
-rw-r--r--src/qml/compiler/compiler.pri26
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp315
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h20
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp26
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h2
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp211
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h282
-rw-r--r--src/qml/compiler/qv4codegen.cpp3937
-rw-r--r--src/qml/compiler/qv4codegen_p.h748
-rw-r--r--src/qml/compiler/qv4compileddata.cpp87
-rw-r--r--src/qml/compiler/qv4compileddata_p.h126
-rw-r--r--src/qml/compiler/qv4compiler.cpp165
-rw-r--r--src/qml/compiler/qv4compiler_p.h35
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp82
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h290
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h467
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp475
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h160
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp592
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h1226
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp1522
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h240
-rw-r--r--src/qml/compiler/qv4isel_p.cpp446
-rw-r--r--src/qml/compiler/qv4isel_p.h218
-rw-r--r--src/qml/compiler/qv4isel_util_p.h241
-rw-r--r--src/qml/compiler/qv4jsir.cpp993
-rw-r--r--src/qml/compiler/qv4jsir_p.h1813
-rw-r--r--src/qml/compiler/qv4ssa.cpp5848
-rw-r--r--src/qml/compiler/qv4ssa_p.h472
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h157
-rw-r--r--src/qml/jit/jit.pri22
-rw-r--r--src/qml/jit/qv4assembler.cpp2251
-rw-r--r--src/qml/jit/qv4assembler_p.h1895
-rw-r--r--src/qml/jit/qv4binop.cpp665
-rw-r--r--src/qml/jit/qv4binop_p.h256
-rw-r--r--src/qml/jit/qv4isel_masm.cpp1684
-rw-r--r--src/qml/jit/qv4isel_masm_p.h319
-rw-r--r--src/qml/jit/qv4jit.cpp1365
-rw-r--r--src/qml/jit/qv4jit_p.h265
-rw-r--r--src/qml/jit/qv4regalloc.cpp1971
-rw-r--r--src/qml/jit/qv4regalloc_p.h140
-rw-r--r--src/qml/jit/qv4registerinfo_p.h112
-rw-r--r--src/qml/jit/qv4targetplatform_p.h707
-rw-r--r--src/qml/jit/qv4unop.cpp166
-rw-r--r--src/qml/jsapi/qjsengine.cpp17
-rw-r--r--src/qml/jsapi/qjsvalue.cpp41
-rw-r--r--src/qml/jsruntime/jsruntime.pri3
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp131
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h33
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp99
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h12
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp30
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h9
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp672
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h52
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp56
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h8
-rw-r--r--src/qml/jsruntime/qv4context.cpp456
-rw-r--r--src/qml/jsruntime/qv4context_p.h245
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp145
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h24
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp596
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h106
-rw-r--r--src/qml/jsruntime/qv4engine.cpp200
-rw-r--r--src/qml/jsruntime/qv4engine_p.h119
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h29
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp76
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h22
-rw-r--r--src/qml/jsruntime/qv4function.cpp82
-rw-r--r--src/qml/jsruntime/qv4function_p.h31
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp331
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h91
-rw-r--r--src/qml/jsruntime/qv4global_p.h25
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp158
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h24
-rw-r--r--src/qml/jsruntime/qv4include.cpp20
-rw-r--r--src/qml/jsruntime/qv4include_p.h2
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h139
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp76
-rw-r--r--src/qml/jsruntime/qv4jsonobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp293
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h31
-rw-r--r--src/qml/jsruntime/qv4managed.cpp1
-rw-r--r--src/qml/jsruntime/qv4managed_p.h15
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp92
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h38
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h2
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp209
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h24
-rw-r--r--src/qml/jsruntime/qv4object.cpp256
-rw-r--r--src/qml/jsruntime/qv4object_p.h90
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp4
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h4
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp485
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h58
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h39
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp7
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h10
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp118
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h20
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp26
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h12
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp175
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h34
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp727
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h15
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h99
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp79
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h (renamed from src/qml/jit/qv4unop_p.h)48
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h79
-rw-r--r--src/qml/jsruntime/qv4script.cpp80
-rw-r--r--src/qml/jsruntime/qv4script_p.h56
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp45
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h8
-rw-r--r--src/qml/jsruntime/qv4string.cpp9
-rw-r--r--src/qml/jsruntime/qv4string_p.h5
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp495
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h54
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp184
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h18
-rw-r--r--src/qml/jsruntime/qv4value.cpp119
-rw-r--r--src/qml/jsruntime/qv4value_p.h348
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp36
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h8
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp1436
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h17
-rw-r--r--src/qml/memory/qv4heap_p.h23
-rw-r--r--src/qml/memory/qv4mm.cpp109
-rw-r--r--src/qml/memory/qv4mm_p.h71
-rw-r--r--src/qml/memory/qv4mmdefs_p.h61
-rw-r--r--src/qml/memory/qv4writebarrier_p.h36
-rw-r--r--src/qml/parser/qqmljsast_p.h3
-rw-r--r--src/qml/qml.pro4
-rw-r--r--src/qml/qml/qqmlbinding.cpp10
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp29
-rw-r--r--src/qml/qml/qqmlcomponent.cpp47
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp31
-rw-r--r--src/qml/qml/qqmldelayedcallqueue_p.h2
-rw-r--r--src/qml/qml/qqmlexpression.cpp9
-rw-r--r--src/qml/qml/qqmlexpression_p.h2
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp44
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h5
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp6
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h2
-rw-r--r--src/qml/qml/qqmllocale.cpp250
-rw-r--r--src/qml/qml/qqmllocale_p.h76
-rw-r--r--src/qml/qml/qqmlmetatype.cpp3
-rw-r--r--src/qml/qml/qqmltypeloader.cpp26
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp9
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp11
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp329
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp548
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h118
-rw-r--r--src/qml/qml/v8/qv4domerrors_p.h3
-rw-r--r--src/qml/qml/v8/qv8engine_p.h4
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp65
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h6
-rw-r--r--src/qml/types/qqmllistmodel.cpp8
-rw-r--r--src/qml/types/qquickworkerscript.cpp49
-rw-r--r--src/qml/util/qqmladaptormodel.cpp62
-rw-r--r--src/qmldevtools/qmldevtools.pro1
-rw-r--r--src/qmltest/quicktestresult.cpp1
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp12
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp689
-rw-r--r--src/quick/items/qquickitem.cpp8
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp41
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp29
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations1
m---------tests/auto/qml/ecmascripttests/test2620
-rw-r--r--tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp13
-rw-r--r--tests/auto/qml/qml.pro3
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp28
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp56
-rw-r--r--tests/auto/quick/qquickflickable/BLACKLIST2
-rw-r--r--tests/manual/v4/fact.2.js2
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp73
-rw-r--r--tools/qmljs/qmljs.cpp148
193 files changed, 16403 insertions, 30486 deletions
diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h
index 6e77a9ffb7..20ddcadae1 100644
--- a/src/3rdparty/masm/assembler/MacroAssembler.h
+++ b/src/3rdparty/masm/assembler/MacroAssembler.h
@@ -810,7 +810,7 @@ public:
Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right)
{
- return branch64(cond, left, TrustedImm64(right));
+ return this->branch64(cond, left, TrustedImm64(right));
}
Jump branchPtr(RelationalCondition cond, RegisterID left, Address right)
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
index d5f4acb3ca..1f94eb9032 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
@@ -718,15 +718,20 @@ public:
void or64(TrustedImm64 imm, RegisterID dest)
{
+ or64(imm, dest, dest);
+ }
+
+ void or64(TrustedImm64 imm, RegisterID src, RegisterID dest)
+ {
LogicalImmediate logicalImm = LogicalImmediate::create64(static_cast<intptr_t>(static_cast<int64_t>(imm.m_value)));
if (logicalImm.isValid()) {
- m_assembler.orr<64>(dest, dest, logicalImm);
+ m_assembler.orr<64>(dest, src, logicalImm);
return;
}
move(imm, getCachedDataTempRegisterIDAndInvalidate());
- m_assembler.orr<64>(dest, dest, dataTempRegister);
+ m_assembler.orr<64>(dest, src, dataTempRegister);
}
void rotateRight64(TrustedImm32 imm, RegisterID srcDst)
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
index fe8170d098..d91122d4a1 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
@@ -815,6 +815,17 @@ public:
store8(src, setupArmAddress(address));
}
+ void store8(RegisterID src, Address address)
+ {
+ store8(src, setupArmAddress(address));
+ }
+
+ void store8(TrustedImm32 imm, Address address)
+ {
+ move(imm, dataTempRegister);
+ store8(dataTempRegister, address);
+ }
+
#if !defined(V4_BOOTSTRAP)
void store8(RegisterID src, void* address)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
index c7c6aae637..002caaae78 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
@@ -292,6 +292,12 @@ public:
or64(imm, dest);
}
+ void or64(TrustedImm64 imm, RegisterID src, RegisterID dest)
+ {
+ move(src, dest);
+ or64(imm, dest);
+ }
+
void rotateRight64(TrustedImm32 imm, RegisterID srcDst)
{
m_assembler.rorq_i8r(imm.m_value, srcDst);
@@ -366,6 +372,13 @@ public:
}
}
+ void urshift64(RegisterID src, TrustedImm32 imm, RegisterID dest)
+ {
+ if (src != dest)
+ move(src, dest);
+ urshift64(imm, dest);
+ }
+
void urshift64(TrustedImm32 imm, RegisterID dest)
{
m_assembler.shrq_i8r(imm.m_value, dest);
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 7c9a77b11a..9d78773fe1 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -58,6 +58,7 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4objectiterator_p.h>
static void initResources()
@@ -164,8 +165,9 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper);
-static void qmlsqldatabase_version(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_version(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
@@ -173,8 +175,9 @@ static void qmlsqldatabase_version(const QV4::BuiltinFunction *, QV4::Scope &sco
RETURN_RESULT(Encode(scope.engine->newString(*r->d()->version)));
}
-static void qmlsqldatabase_rows_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_rows_length(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
@@ -191,20 +194,22 @@ static void qmlsqldatabase_rows_length(const QV4::BuiltinFunction *, QV4::Scope
RETURN_RESULT(Encode(s));
}
-static void qmlsqldatabase_rows_forwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_rows_forwardOnly(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
RETURN_RESULT(Encode(r->d()->sqlQuery->isForwardOnly()));
}
-static void qmlsqldatabase_rows_setForwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_rows_setForwardOnly(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- if (callData->argc < 1)
+ if (callData->argc() < 1)
RETURN_RESULT(scope.engine->throwTypeError());
r->d()->sqlQuery->setForwardOnly(callData->args[0].toBoolean());
@@ -249,13 +254,14 @@ ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(const Managed *m, uint index, b
return qmlsqldatabase_rows_index(r, r->engine(), index, hasProperty);
}
-static void qmlsqldatabase_rows_item(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_rows_item(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- RETURN_RESULT(qmlsqldatabase_rows_index(r, scope.engine, callData->argc ? callData->args[0].toUInt32() : 0));
+ RETURN_RESULT(qmlsqldatabase_rows_index(r, scope.engine, callData->argc() ? callData->args[0].toUInt32() : 0));
}
static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValue &value)
@@ -267,8 +273,9 @@ static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValu
return engine->toVariant(value, /*typehint*/-1);
}
-static void qmlsqldatabase_executeSql(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_executeSql(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Query)
V4THROW_REFERENCE("Not a SQLDatabase::Query object");
@@ -278,7 +285,7 @@ static void qmlsqldatabase_executeSql(const QV4::BuiltinFunction *, QV4::Scope &
QSqlDatabase db = *r->d()->database;
- QString sql = callData->argc ? callData->args[0].toQString() : QString();
+ QString sql = callData->argc() ? callData->args[0].toQString() : QString();
if (r->d()->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
V4THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QQmlEngine::tr("Read-only Transaction"));
@@ -290,7 +297,7 @@ static void qmlsqldatabase_executeSql(const QV4::BuiltinFunction *, QV4::Scope &
ScopedValue result(scope, Primitive::undefinedValue());
if (query.prepare(sql)) {
- if (callData->argc > 1) {
+ if (callData->argc() > 1) {
ScopedValue values(scope, callData->args[1]);
if (values->as<ArrayObject>()) {
ScopedArrayObject array(scope, values);
@@ -377,9 +384,10 @@ struct TransactionRollback {
};
-static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- if (callData->argc < 2)
+ Scope scope(b);
+ if (callData->argc() < 2)
RETURN_UNDEFINED();
Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject);
@@ -389,7 +397,7 @@ static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scop
QSqlDatabase db = *r->d()->database;
QString from_version = callData->args[0].toQString();
QString to_version = callData->args[1].toQString();
- ScopedFunctionObject callback(scope, callData->argc > 2 ? callData->args[2] : Primitive::undefinedValue());
+ ScopedFunctionObject callback(scope, callData->argc() > 2 ? callData->args[2] : Primitive::undefinedValue());
if (from_version != *r->d()->version)
V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(*r->d()->version));
@@ -406,12 +414,12 @@ static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scop
ok = false;
db.transaction();
- ScopedCallData callData(scope, 1);
- callData->thisObject = scope.engine->globalObject;
- callData->args[0] = w;
+ JSCallData jsCall(scope, 1);
+ *jsCall->thisObject = scope.engine->globalObject;
+ jsCall->args[0] = w;
TransactionRollback rollbackOnException(&db, &w->d()->inTransaction);
- callback->call(scope, callData);
+ callback->call(jsCall);
rollbackOnException.clear();
if (!db.commit()) {
db.rollback();
@@ -433,13 +441,14 @@ static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scop
RETURN_UNDEFINED();
}
-static void qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData, bool readOnly)
+static ReturnedValue qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *b, QV4::CallData *callData, bool readOnly)
{
+ Scope scope(b);
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
- const FunctionObject *callback = callData->argc ? callData->args[0].as<FunctionObject>() : 0;
+ const FunctionObject *callback = callData->argc() ? callData->args[0].as<FunctionObject>() : 0;
if (!callback)
V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR, QQmlEngine::tr("transaction: missing callback"));
@@ -455,11 +464,11 @@ static void qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *, QV4:
db.transaction();
if (callback) {
- ScopedCallData callData(scope, 1);
- callData->thisObject = scope.engine->globalObject;
- callData->args[0] = w;
+ JSCallData jsCall(scope, 1);
+ *jsCall->thisObject = scope.engine->globalObject;
+ jsCall->args[0] = w;
TransactionRollback rollbackOnException(&db, &w->d()->inTransaction);
- callback->call(scope, callData);
+ callback->call(jsCall);
rollbackOnException.clear();
if (!db.commit())
@@ -469,14 +478,14 @@ static void qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *, QV4:
RETURN_UNDEFINED();
}
-static void qmlsqldatabase_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_transaction(const QV4::BuiltinFunction *f, QV4::CallData *callData)
{
- qmlsqldatabase_transaction_shared(f, scope, callData, false);
+ return qmlsqldatabase_transaction_shared(f, callData, false);
}
-static void qmlsqldatabase_read_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData)
+static ReturnedValue qmlsqldatabase_read_transaction(const QV4::BuiltinFunction *f, QV4::CallData *callData)
{
- qmlsqldatabase_transaction_shared(f, scope, callData, true);
+ return qmlsqldatabase_transaction_shared(f, callData, true);
}
QQmlSqlDatabaseData::QQmlSqlDatabaseData(ExecutionEngine *v4)
@@ -754,10 +763,10 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
*db->d()->version = version;
if (created && dbcreationCallback) {
- ScopedCallData callData(scope, 1);
- callData->thisObject = scope.engine->globalObject;
- callData->args[0] = db;
- dbcreationCallback->call(scope, callData);
+ JSCallData jsCall(scope, 1);
+ *jsCall->thisObject = scope.engine->globalObject;
+ jsCall->args[0] = db;
+ dbcreationCallback->call(jsCall);
}
args->setReturnValue(db.asReturnedValue());
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index e8376f1c27..acb37f25bf 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -267,22 +267,25 @@ QT_BEGIN_NAMESPACE
The currentSize of the particle, interpolating between startSize and endSize based on the currentTime.
*/
-
+namespace QV4 {
+namespace Heap {
+struct QV4ParticleData : QV4::Object::Data {
+ void init(QQuickParticleData *datum, QQuickParticleSystem* particleSystem)
+ {
+ Object::init();
+ this->datum = datum;
+ this->particleSystem = particleSystem;
+ }
+ QQuickParticleData* datum;//TODO: Guard needed?
+ QQuickParticleSystem* particleSystem;
+};
+}
+}
//### Particle data handles are not locked to within certain scopes like QQuickContext2D, but there's no way to reload either...
struct QV4ParticleData : public QV4::Object
{
- struct Data : QV4::Object::Data {
- void init(QQuickParticleData *datum, QQuickParticleSystem* particleSystem)
- {
- Object::init();
- this->datum = datum;
- this->particleSystem = particleSystem;
- }
- QQuickParticleData* datum;//TODO: Guard needed?
- QQuickParticleSystem* particleSystem;
- };
- V4_OBJECT(QV4::Object)
+ V4_OBJECT2(QV4ParticleData, QV4::Object)
};
DEFINE_OBJECT_VTABLE(QV4ParticleData);
@@ -296,8 +299,9 @@ public:
QV4::PersistentValue proto;
};
-static void particleData_discard(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static QV4::ReturnedValue particleData_discard(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
@@ -307,8 +311,9 @@ static void particleData_discard(const QV4::BuiltinFunction *, QV4::Scope &scope
RETURN_RESULT(QV4::Encode::undefined());
}
-static void particleData_lifeLeft(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static QV4::ReturnedValue particleData_lifeLeft(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
@@ -317,8 +322,9 @@ static void particleData_lifeLeft(const QV4::BuiltinFunction *, QV4::Scope &scop
RETURN_RESULT(QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem)));
}
-static void particleData_curSize(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static QV4::ReturnedValue particleData_curSize(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
@@ -326,8 +332,9 @@ static void particleData_curSize(const QV4::BuiltinFunction *, QV4::Scope &scope
RETURN_RESULT(QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem)));
}
-#define COLOR_GETTER_AND_SETTER(VAR, NAME) static void particleData_get_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
+#define COLOR_GETTER_AND_SETTER(VAR, NAME) static QV4::ReturnedValue particleData_get_ ## NAME (const QV4::BuiltinFunction *b, QV4::CallData *callData) \
{ \
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
@@ -335,20 +342,22 @@ static void particleData_curSize(const QV4::BuiltinFunction *, QV4::Scope &scope
RETURN_RESULT(QV4::Encode((r->d()->datum->color. VAR )/255.0));\
}\
\
-static void particleData_set_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
+static QV4::ReturnedValue particleData_set_ ## NAME (const QV4::BuiltinFunction *b, QV4::CallData *callData)\
{\
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- double d = callData->argc ? callData->args[0].toNumber() : 0; \
+ double d = callData->argc() ? callData->args[0].toNumber() : 0; \
r->d()->datum->color. VAR = qMin(255, qMax(0, (int)::floor(d * 255.0)));\
RETURN_UNDEFINED(); \
}
-#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
+#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData) \
{ \
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
@@ -356,18 +365,20 @@ static void particleData_set_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope
RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
}\
\
-static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
+static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData)\
{\
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> VARIABLE = (callData->argc && callData->args[0].toBoolean()) ? 1.0 : 0.0;\
+ r->d()->datum-> VARIABLE = (callData->argc() && callData->args[0].toBoolean()) ? 1.0 : 0.0;\
RETURN_UNDEFINED(); \
}
-#define FLOAT_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
+#define FLOAT_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData) \
{ \
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
@@ -375,18 +386,20 @@ static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Sc
RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
}\
\
-static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
+static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData)\
{\
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> VARIABLE = callData->argc ? callData->args[0].toNumber() : qt_qnan();\
+ r->d()->datum-> VARIABLE = callData->argc() ? callData->args[0].toNumber() : qt_qnan();\
RETURN_UNDEFINED(); \
}
-#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
+#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static QV4::ReturnedValue particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData) \
{ \
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
@@ -394,13 +407,14 @@ static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Sc
RETURN_RESULT(QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem)));\
}\
\
-static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
+static QV4::ReturnedValue particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *b, QV4::CallData *callData)\
{\
+ QV4::Scope scope(b); \
QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> SETTER (callData->argc ? callData->args[0].toNumber() : qt_qnan(), r->d()->particleSystem);\
+ r->d()->datum-> SETTER (callData->argc() ? callData->args[0].toNumber() : qt_qnan(), r->d()->particleSystem);\
RETURN_UNDEFINED(); \
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 8ff0a3ca64..a538956e8e 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -55,47 +55,43 @@
QT_BEGIN_NAMESPACE
-QV4::SimpleCallContext *QV4DataCollector::findContext(int frame)
+QV4::CppStackFrame *QV4DataCollector::findFrame(int frame)
{
- QV4::ExecutionContext *ctx = engine()->currentContext;
- while (ctx) {
- QV4::SimpleCallContext *cCtxt = ctx->asSimpleCallContext();
- if (cCtxt && cCtxt->d()->v4Function) {
- if (frame < 1)
- return cCtxt;
- --frame;
- }
- ctx = engine()->parentContext(ctx);
+ QV4::CppStackFrame *f = engine()->currentStackFrame;
+ while (f && frame) {
+ --frame;
+ f = f->parent;
}
+ return f;
+}
- return 0;
+QV4::Heap::ExecutionContext *QV4DataCollector::findContext(int frame)
+{
+ QV4::CppStackFrame *f = findFrame(frame);
+
+ return f ? f->context()->d() : 0;
}
-QV4::Heap::SimpleCallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, int scope)
+QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::Heap::ExecutionContext *ctx, int scope)
{
- if (!ctxt)
+ if (!ctx)
return 0;
- QV4::Scope s(ctxt);
- QV4::ScopedContext ctx(s, ctxt);
for (; scope > 0 && ctx; --scope)
- ctx = ctx->d()->outer;
+ ctx = ctx->outer;
- return (ctx && ctx->d()) ? ctx->asSimpleCallContext()->d() : 0;
+ return (ctx && ctx->type == QV4::Heap::ExecutionContext::Type_CallContext) ?
+ static_cast<QV4::Heap::CallContext *>(ctx) : 0;
}
QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes(int frame)
{
QVector<QV4::Heap::ExecutionContext::ContextType> types;
- QV4::Scope scope(engine());
- QV4::SimpleCallContext *sctxt = findContext(frame);
- if (!sctxt || sctxt->d()->type < QV4::Heap::ExecutionContext::Type_QmlContext)
- return types;
+ QV4::Heap::ExecutionContext *it = findFrame(frame)->context()->d();
- QV4::ScopedContext it(scope, sctxt);
- for (; it; it = it->d()->outer)
- types.append(QV4::Heap::ExecutionContext::ContextType(it->d()->type));
+ for (; it; it = it->outer)
+ types.append(QV4::Heap::ExecutionContext::ContextType(it->type));
return types;
}
@@ -109,7 +105,6 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s
return 4;
case QV4::Heap::ExecutionContext::Type_WithContext:
return 2;
- case QV4::Heap::ExecutionContext::Type_SimpleCallContext:
case QV4::Heap::ExecutionContext::Type_CallContext:
return 1;
case QV4::Heap::ExecutionContext::Type_QmlContext:
@@ -267,27 +262,17 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
QStringList names;
QV4::Scope scope(engine());
+
QV4::Scoped<QV4::CallContext> ctxt(scope, findScope(findContext(frameNr), scopeNr));
if (!ctxt)
return false;
Refs collectedRefs;
QV4::ScopedValue v(scope);
- int nFormals = ctxt->formalCount();
- for (unsigned i = 0, ei = nFormals; i != ei; ++i) {
- QString qName;
- if (QV4::Identifier *name = ctxt->formals()[nFormals - i - 1])
- qName = name->string;
- names.append(qName);
- v = ctxt->argument(i);
- collectedRefs.append(collect(v));
- }
-
- for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) {
- QString qName;
- if (QV4::Identifier *name = ctxt->variables()[i])
- qName = name->string;
- names.append(qName);
+ QV4::InternalClass *ic = ctxt->internalClass();
+ for (uint i = 0; i < ic->size; ++i) {
+ QString name = ic->nameMap[i]->string;
+ names.append(name);
v = ctxt->d()->locals[i];
collectedRefs.append(collect(v));
}
@@ -338,7 +323,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
QV4::Scope scope(engine());
QV4::ScopedContext ctxt(scope, findContext(frameNr));
while (ctxt) {
- if (QV4::SimpleCallContext *cCtxt = ctxt->asSimpleCallContext()) {
+ if (QV4::CallContext *cCtxt = ctxt->asCallContext()) {
if (cCtxt->d()->activation)
break;
}
@@ -346,7 +331,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
}
if (ctxt) {
- QV4::ScopedValue o(scope, ctxt->asSimpleCallContext()->d()->activation);
+ QV4::ScopedValue o(scope, ctxt->d()->activation);
frame[QLatin1String("receiver")] = toRef(collect(o));
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
index de12e8d527..87be009de5 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
@@ -58,11 +58,12 @@ public:
typedef uint Ref;
typedef QVector<uint> Refs;
- static QV4::Heap::SimpleCallContext *findScope(QV4::ExecutionContext *ctxt, int scope);
+ static QV4::Heap::CallContext *findScope(QV4::Heap::ExecutionContext *ctxt, int scope);
static int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType);
QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes(int frame);
- QV4::SimpleCallContext *findContext(int frame);
+ QV4::Heap::ExecutionContext *findContext(int frame);
+ QV4::CppStackFrame *findFrame(int frame);
QV4DataCollector(QV4::ExecutionEngine *engine);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index b82df9c6a9..1581cf637d 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -115,7 +115,7 @@ void QV4Debugger::resume(Speed speed)
if (!m_returnedValue.isUndefined())
m_returnedValue.set(m_engine, QV4::Encode::undefined());
- m_currentContext.set(m_engine, *m_engine->currentContext);
+ m_currentFrame = m_engine->currentStackFrame;
m_stepping = speed;
m_runningCondition.wakeAll();
}
@@ -158,7 +158,7 @@ QV4Debugger::ExecutionState QV4Debugger::currentExecutionState() const
{
ExecutionState state;
state.fileName = getFunction()->sourceFile();
- state.lineNumber = engine()->current->lineNumber;
+ state.lineNumber = engine()->currentStackFrame->lineNumber();
return state;
}
@@ -187,7 +187,7 @@ void QV4Debugger::maybeBreakAtInstruction()
switch (m_stepping) {
case StepOver:
- if (m_currentContext.asManaged()->d() != m_engine->current)
+ if (m_currentFrame != m_engine->currentStackFrame)
break;
// fall through
case StepIn:
@@ -203,7 +203,8 @@ void QV4Debugger::maybeBreakAtInstruction()
pauseAndWait(PauseRequest);
} else if (m_haveBreakPoints) {
if (QV4::Function *f = getFunction()) {
- const int lineNumber = engine()->current->lineNumber;
+ // lineNumber will be negative for Ret instructions, so those won't match
+ const int lineNumber = engine()->currentStackFrame->lineNumber();
if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber))
pauseAndWait(BreakPointHit);
}
@@ -216,9 +217,8 @@ void QV4Debugger::enteringFunction()
return;
QMutexLocker locker(&m_lock);
- if (m_stepping == StepIn) {
- m_currentContext.set(m_engine, *m_engine->currentContext);
- }
+ if (m_stepping == StepIn)
+ m_currentFrame = m_engine->currentStackFrame;
}
void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal)
@@ -229,13 +229,8 @@ void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal)
QMutexLocker locker(&m_lock);
- if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) {
- if (QV4::ExecutionContext *parentContext
- = m_engine->parentContext(m_engine->currentContext)) {
- m_currentContext.set(m_engine, *parentContext);
- } else {
- m_currentContext.clear();
- }
+ if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) {
+ m_currentFrame = m_currentFrame->parent;
m_stepping = StepOver;
m_returnedValue.set(m_engine, retVal);
}
@@ -255,10 +250,8 @@ void QV4Debugger::aboutToThrow()
QV4::Function *QV4Debugger::getFunction() const
{
- QV4::Scope scope(m_engine);
- QV4::ExecutionContext *context = m_engine->currentContext;
- if (QV4::Function *function = context->getFunction())
- return function;
+ if (m_engine->currentStackFrame)
+ return m_engine->currentStackFrame->v4Function;
else
return m_engine->globalCode;
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
index 06014302e6..4a755f2b72 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
@@ -150,7 +150,7 @@ private:
void runJobUnpaused();
QV4::ExecutionEngine *m_engine;
- QV4::PersistentValue m_currentContext;
+ QV4::CppStackFrame *m_currentFrame = 0;
QMutex m_lock;
QWaitCondition m_runningCondition;
State m_state;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index 9a34d5770a..87e75c49b5 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -79,20 +79,19 @@ void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseR
case QV4Debugger::PauseRequest:
case QV4Debugger::BreakPointHit: {
event.insert(QStringLiteral("event"), QStringLiteral("break"));
- QVector<QV4::StackFrame> frames = debugger->stackTrace(1);
- if (frames.isEmpty())
+ QV4::CppStackFrame *frame = debugger->engine()->currentStackFrame;
+ if (!frame)
break;
- const QV4::StackFrame &topFrame = frames.first();
- body.insert(QStringLiteral("invocationText"), topFrame.function);
- body.insert(QStringLiteral("sourceLine"), topFrame.line - 1);
- if (topFrame.column > 0)
- body.insert(QStringLiteral("sourceColumn"), topFrame.column);
+ body.insert(QStringLiteral("invocationText"), frame->function());
+ body.insert(QStringLiteral("sourceLine"), qAbs(frame->lineNumber()) - 1);
+// if (frame->column > 0)
+// body.insert(QStringLiteral("sourceColumn"), frame->column);
QJsonArray breakPoints;
- foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line))
+ foreach (int breakPointId, breakPointIds(frame->source(), frame->lineNumber()))
breakPoints.push_back(breakPointId);
body.insert(QStringLiteral("breakpoints"), breakPoints);
- script.insert(QStringLiteral("name"), topFrame.source);
+ script.insert(QStringLiteral("name"), frame->source());
} break;
case QV4Debugger::Throwing:
// TODO: complete this!
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
index d57d7e7406..6f254c9e28 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
@@ -44,6 +44,7 @@
#include <private/qv4qmlcontext_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmldebugservice_p.h>
+#include <private/qv4jscall_p.h>
#include <QtQml/qqmlengine.h>
@@ -63,26 +64,21 @@ void JavaScriptJob::run()
{
QV4::Scope scope(engine);
- QV4::ExecutionContextSaver saver(scope);
-
- QV4::ExecutionContext *ctx = engine->currentContext;
+ QV4::ScopedContext ctx(scope, engine->currentStackFrame ? engine->currentContext()
+ : engine->rootContext());
QObject scopeObject;
- if (frameNr > 0) {
- for (int i = 0; i < frameNr; ++i) {
- ctx = engine->parentContext(ctx);
- }
- engine->pushContext(ctx);
- ctx = engine->currentContext;
- }
+ QV4::CppStackFrame *frame = engine->currentStackFrame;
+
+ for (int i = 0; frame && i < frameNr; ++i)
+ frame = frame->parent;
+ if (frameNr > 0 && frame)
+ ctx = static_cast<QV4::ExecutionContext *>(&frame->jsFrame->context);
if (context >= 0) {
QQmlContext *extraContext = qmlContext(QQmlDebugService::objectForId(context));
- if (extraContext) {
- engine->pushContext(QV4::QmlContext::create(ctx, QQmlContextData::get(extraContext),
- &scopeObject));
- ctx = engine->currentContext;
- }
+ if (extraContext)
+ ctx = QV4::QmlContext::create(ctx, QQmlContextData::get(extraContext), &scopeObject);
} else if (frameNr < 0) { // Use QML context if available
QQmlEngine *qmlEngine = engine->qmlEngine();
if (qmlEngine) {
@@ -102,18 +98,15 @@ void JavaScriptJob::run()
}
}
}
- if (!engine->qmlContext()) {
- engine->pushContext(QV4::QmlContext::create(ctx, QQmlContextData::get(qmlRootContext),
- &scopeObject));
- ctx = engine->currentContext;
- }
- engine->pushContext(ctx->newWithContext(withContext->toObject(engine)));
- ctx = engine->currentContext;
+ if (!engine->qmlContext())
+ ctx = QV4::QmlContext::create(ctx, QQmlContextData::get(qmlRootContext), &scopeObject);
}
}
- QV4::Script script(ctx, this->script);
- script.strictMode = ctx->d()->strictMode;
+ QV4::Script script(ctx, QV4::Compiler::EvalCode, this->script);
+ if (const QV4::Function *function = frame ? frame->v4Function : engine->globalCode)
+ script.strictMode = function->isStrict();
+
// In order for property lookups in QML to work, we need to disable fast v4 lookups. That
// is a side-effect of inheritContext.
script.inheritContext = true;
@@ -214,12 +207,15 @@ void ValueLookupJob::run()
// set if the engine is currently executing QML code.
QScopedPointer<QObject> scopeObject;
QV4::ExecutionEngine *engine = collector->engine();
+ QV4::Scope scope(engine);
+ QV4::Heap::ExecutionContext *qmlContext = 0;
if (engine->qmlEngine() && !engine->qmlContext()) {
scopeObject.reset(new QObject);
- engine->pushContext(QV4::QmlContext::create(engine->currentContext,
+ qmlContext = QV4::QmlContext::create(engine->currentContext(),
QQmlContextData::get(engine->qmlEngine()->rootContext()),
- scopeObject.data()));
+ scopeObject.data());
}
+ QV4::ScopedStackFrame frame(scope, qmlContext);
for (const QJsonValue &handle : handles) {
QV4DataCollector::Ref ref = handle.toInt();
if (!collector->isValidRef(ref)) {
@@ -229,8 +225,6 @@ void ValueLookupJob::run()
result[QString::number(ref)] = collector->lookupRef(ref, true);
}
flushRedundantRefs();
- if (scopeObject)
- engine->popContext();
}
const QString &ValueLookupJob::exceptionMessage() const
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index fe00ed1e28..fca811cb28 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -42,7 +42,6 @@
#include "qqmlengine.h"
#include <private/qv4engine_p.h>
-#include <private/qv4isel_moth_p.h>
#include <private/qv4function_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv8engine_p.h>
@@ -706,7 +705,6 @@ void QV4DebugServiceImpl::engineAdded(QJSEngine *engine)
QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle());
if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
if (ee) {
- ee->iselFactory.reset(new QV4::Moth::ISelFactory);
QV4Debugger *debugger = new QV4Debugger(ee);
if (state() == Enabled)
ee->setDebugger(debugger);
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index cff1728f49..eeedb59ce6 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -49,7 +49,6 @@
#include <private/qv4objectiterator_p.h>
#include <private/qv4identifier_p.h>
#include <private/qv4runtime_p.h>
-#include <private/qv4isel_moth_p.h>
#include <private/qqmldebugpacket_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
@@ -208,7 +207,7 @@ private:
void handleDebuggerDeleted(QObject *debugger);
- void evaluateExpression(QV4::Scope &scope, const QString &expression);
+ QV4::ReturnedValue evaluateExpression(const QString &expression);
bool checkCondition(const QString &expression);
QStringList breakOnSignals;
@@ -230,7 +229,7 @@ private:
QV4::ExecutionEngine *m_engine;
QQmlNativeDebugServiceImpl *m_service;
- QV4::PersistentValue m_currentContext;
+ QV4::CppStackFrame *m_currentFrame = 0;
Speed m_stepping;
bool m_pauseRequested;
bool m_runningJob;
@@ -241,29 +240,31 @@ private:
bool NativeDebugger::checkCondition(const QString &expression)
{
QV4::Scope scope(m_engine);
- evaluateExpression(scope, expression);
- return scope.result.booleanValue();
+ QV4::ScopedValue r(scope, evaluateExpression(expression));
+ return r->booleanValue();
}
-void NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression)
+QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression)
{
+ QV4::Scope scope(m_engine);
m_runningJob = true;
- QV4::ExecutionContextSaver saver(scope);
-
- QV4::ExecutionContext *ctx = m_engine->currentContext;
- m_engine->pushContext(ctx);
+ QV4::ExecutionContext *ctx = m_engine->currentStackFrame ? m_engine->currentContext()
+ : m_engine->rootContext();
- QV4::Script script(ctx, expression);
- script.strictMode = ctx->d()->strictMode;
+ QV4::Script script(ctx, QV4::Compiler::EvalCode, expression);
+ if (const QV4::Function *function = m_engine->currentStackFrame
+ ? m_engine->currentStackFrame->v4Function : m_engine->globalCode)
+ script.strictMode = function->isStrict();
// In order for property lookups in QML to work, we need to disable fast v4 lookups.
// That is a side-effect of inheritContext.
script.inheritContext = true;
script.parse();
if (!m_engine->hasException)
- scope.result = script.run();
+ return script.run();
m_runningJob = false;
+ return QV4::Encode::undefined();
}
NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
@@ -314,19 +315,19 @@ void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd,
handleContinue(response, NotStepping);
}
-static QString encodeContext(QV4::ExecutionContext *executionContext)
+static QString encodeFrame(QV4::CppStackFrame *f)
{
QQmlDebugPacket ds;
- ds << quintptr(executionContext);
+ ds << quintptr(f);
return QString::fromLatin1(ds.data().toHex());
}
-static void decodeContext(const QString &context, QV4::ExecutionContext **executionContext)
+static void decodeFrame(const QString &f, QV4::CppStackFrame **frame)
{
- quintptr rawContext;
- QQmlDebugPacket ds(QByteArray::fromHex(context.toLatin1()));
- ds >> rawContext;
- *executionContext = reinterpret_cast<QV4::ExecutionContext *>(rawContext);
+ quintptr rawFrame;
+ QQmlDebugPacket ds(QByteArray::fromHex(f.toLatin1()));
+ ds >> rawFrame;
+ *frame = reinterpret_cast<QV4::CppStackFrame *>(rawFrame);
}
void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments)
@@ -334,25 +335,24 @@ void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &a
int limit = arguments.value(QLatin1String("limit")).toInt(0);
QJsonArray frameArray;
- QV4::ExecutionContext *executionContext = m_engine->currentContext;
- for (int i = 0; i < limit && executionContext; ++i) {
- if (QV4::Function *function = executionContext->getFunction()) {
+ QV4::CppStackFrame *f= m_engine->currentStackFrame;
+ for (int i = 0; i < limit && f; ++i) {
+ QV4::Function *function = f->v4Function;
- QJsonObject frame;
- frame.insert(QStringLiteral("language"), QStringLiteral("js"));
- frame.insert(QStringLiteral("context"), encodeContext(executionContext));
+ QJsonObject frame;
+ frame.insert(QStringLiteral("language"), QStringLiteral("js"));
+ frame.insert(QStringLiteral("context"), encodeFrame(f));
- if (QV4::Heap::String *functionName = function->name())
- frame.insert(QStringLiteral("function"), functionName->toQString());
- frame.insert(QStringLiteral("file"), function->sourceFile());
+ if (QV4::Heap::String *functionName = function->name())
+ frame.insert(QStringLiteral("function"), functionName->toQString());
+ frame.insert(QStringLiteral("file"), function->sourceFile());
- int line = executionContext->d()->lineNumber;
- frame.insert(QStringLiteral("line"), (line < 0 ? -line : line));
+ int line = f->lineNumber();
+ frame.insert(QStringLiteral("line"), (line < 0 ? -line : line));
- frameArray.push_back(frame);
- }
+ frameArray.push_back(frame);
- executionContext = m_engine->parentContext(executionContext);
+ f = f->parent;
}
response->insert(QStringLiteral("frames"), frameArray);
@@ -457,15 +457,15 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri
void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &arguments)
{
TRACE_PROTOCOL("Build variables");
- QV4::ExecutionContext *executionContext = 0;
- decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext);
- if (!executionContext) {
- setError(response, QStringLiteral("No execution context passed"));
+ QV4::CppStackFrame *frame = 0;
+ decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame);
+ if (!frame) {
+ setError(response, QStringLiteral("No stack frame passed"));
return;
}
- TRACE_PROTOCOL("Context: " << executionContext);
+ TRACE_PROTOCOL("Context: " << frame);
- QV4::ExecutionEngine *engine = executionContext->engine();
+ QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine;
if (!engine) {
setError(response, QStringLiteral("No execution engine passed"));
return;
@@ -481,28 +481,16 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
QJsonArray output;
QV4::Scope scope(engine);
- if (QV4::SimpleCallContext *callContext = executionContext->asSimpleCallContext()) {
- QV4::Value thisObject = callContext->thisObject();
- collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
- QV4::Identifier *const *variables = callContext->variables();
- QV4::Identifier *const *formals = callContext->formals();
- if (callContext->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) {
- QV4::CallContext *ctx = static_cast<QV4::CallContext *>(callContext);
- for (unsigned i = 0, ei = ctx->variableCount(); i != ei; ++i) {
- QString qName;
- if (QV4::Identifier *name = variables[i])
- qName = name->string;
- QV4::Value val = ctx->d()->locals[i];
- collector.collect(&output, QString(), qName, val);
- }
- }
- for (unsigned i = 0, ei = callContext->formalCount(); i != ei; ++i) {
- QString qName;
- if (QV4::Identifier *name = formals[i])
- qName = name->string;
- QV4::ReturnedValue rval = callContext->argument(i);
- QV4::ScopedValue sval(scope, rval);
- collector.collect(&output, QString(), qName, *sval);
+ QV4::ScopedValue thisObject(scope, frame->thisObject());
+ collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
+ QV4::Scoped<QV4::CallContext> callContext(scope, frame->callContext());
+ if (callContext) {
+ QV4::InternalClass *ic = callContext->internalClass();
+ QV4::ScopedValue v(scope);
+ for (uint i = 0; i < ic->size; ++i) {
+ QString name = ic->nameMap[i]->string;
+ v = callContext->d()->locals[i];
+ collector.collect(&output, QString(), name, v);
}
}
@@ -512,15 +500,15 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject &arguments)
{
TRACE_PROTOCOL("Evaluate expressions");
- QV4::ExecutionContext *executionContext = 0;
- decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext);
- if (!executionContext) {
- setError(response, QStringLiteral("No execution context passed"));
+ QV4::CppStackFrame *frame = 0;
+ decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame);
+ if (!frame) {
+ setError(response, QStringLiteral("No stack frame passed"));
return;
}
TRACE_PROTOCOL("Context: " << executionContext);
- QV4::ExecutionEngine *engine = executionContext->engine();
+ QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine;
if (!engine) {
setError(response, QStringLiteral("No execution engine passed"));
return;
@@ -543,8 +531,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject
TRACE_PROTOCOL("Evaluate expression: " << expression);
m_runningJob = true;
- evaluateExpression(scope, expression);
- QV4::ScopedValue result(scope, scope.result);
+ QV4::ScopedValue result(scope, evaluateExpression(expression));
m_runningJob = false;
if (result->isUndefined()) {
@@ -595,7 +582,7 @@ void NativeDebugger::handleContinue(QJsonObject *response, Speed speed)
if (!m_returnedValue.isUndefined())
m_returnedValue.set(m_engine, QV4::Encode::undefined());
- m_currentContext.set(m_engine, *m_engine->currentContext);
+ m_currentFrame = m_engine->currentStackFrame;
m_stepping = speed;
}
@@ -605,7 +592,7 @@ void NativeDebugger::maybeBreakAtInstruction()
return;
if (m_stepping == StepOver) {
- if (m_currentContext.asManaged()->d() == m_engine->current)
+ if (m_currentFrame == m_engine->currentStackFrame)
pauseAndWait();
return;
}
@@ -623,7 +610,8 @@ void NativeDebugger::maybeBreakAtInstruction()
if (m_service->m_breakHandler->m_haveBreakPoints) {
if (QV4::Function *function = getFunction()) {
- const int lineNumber = m_engine->current->lineNumber;
+ // lineNumber will be negative for Ret instructions, so those won't match
+ const int lineNumber = m_engine->currentStackFrame->lineNumber();
if (reallyHitTheBreakPoint(function, lineNumber))
pauseAndWait();
}
@@ -636,7 +624,7 @@ void NativeDebugger::enteringFunction()
return;
if (m_stepping == StepIn) {
- m_currentContext.set(m_engine, *m_engine->currentContext);
+ m_currentFrame = m_engine->currentStackFrame;
}
}
@@ -645,8 +633,8 @@ void NativeDebugger::leavingFunction(const QV4::ReturnedValue &retVal)
if (m_runningJob)
return;
- if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) {
- m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext));
+ if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) {
+ m_currentFrame = m_currentFrame->parent;
m_stepping = StepOver;
m_returnedValue.set(m_engine, retVal);
}
@@ -668,9 +656,8 @@ void NativeDebugger::aboutToThrow()
QV4::Function *NativeDebugger::getFunction() const
{
- QV4::ExecutionContext *context = m_engine->currentContext;
- if (QV4::Function *function = context->getFunction())
- return function;
+ if (m_engine->currentStackFrame)
+ return m_engine->currentStackFrame->v4Function;
else
return m_engine->globalCode;
}
@@ -681,12 +668,11 @@ void NativeDebugger::pauseAndWait()
event.insert(QStringLiteral("event"), QStringLiteral("break"));
event.insert(QStringLiteral("language"), QStringLiteral("js"));
- if (QV4::ExecutionContext *executionContext = m_engine->currentContext) {
- if (QV4::Function *function = executionContext->getFunction()) {
- event.insert(QStringLiteral("file"), function->sourceFile());
- int line = executionContext->d()->lineNumber;
- event.insert(QStringLiteral("line"), (line < 0 ? -line : line));
- }
+ if (QV4::CppStackFrame *frame = m_engine->currentStackFrame) {
+ QV4::Function *function = frame->v4Function;
+ event.insert(QStringLiteral("file"), function->sourceFile());
+ int line = frame->lineNumber();
+ event.insert(QStringLiteral("line"), (line < 0 ? -line : line));
}
m_service->emitAsynchronousMessageToClient(event);
@@ -731,7 +717,6 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeAdded(QJSEngine *engine)
TRACE_PROTOCOL("Adding execution engine" << ee);
if (ee) {
NativeDebugger *debugger = new NativeDebugger(this, ee);
- ee->iselFactory.reset(new QV4::Moth::ISelFactory);
if (state() == Enabled)
ee->setDebugger(debugger);
m_debuggers.append(QPointer<NativeDebugger>(debugger));
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index d9b985e33b..0d63d3b76f 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -2,26 +2,24 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
HEADERS += \
+ $$PWD/qv4bytecodegenerator_p.h \
$$PWD/qv4compileddata_p.h \
$$PWD/qv4compiler_p.h \
+ $$PWD/qv4compilercontext_p.h \
+ $$PWD/qv4compilercontrolflow_p.h \
+ $$PWD/qv4compilerscanfunctions_p.h \
$$PWD/qv4codegen_p.h \
- $$PWD/qv4isel_p.h \
- $$PWD/qv4jsir_p.h \
- $$PWD/qv4isel_util_p.h \
- $$PWD/qv4ssa_p.h \
$$PWD/qqmlirbuilder_p.h \
- $$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4jssimplifier_p.h
+ $$PWD/qqmltypecompiler_p.h
SOURCES += \
+ $$PWD/qv4bytecodegenerator.cpp \
$$PWD/qv4compileddata.cpp \
$$PWD/qv4compiler.cpp \
+ $$PWD/qv4compilercontext.cpp \
+ $$PWD/qv4compilerscanfunctions.cpp \
$$PWD/qv4codegen.cpp \
- $$PWD/qv4isel_p.cpp \
- $$PWD/qv4jsir.cpp \
- $$PWD/qv4ssa.cpp \
- $$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4jssimplifier.cpp
+ $$PWD/qqmlirbuilder.cpp
!qmldevtools_build {
@@ -46,11 +44,9 @@ qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl
qmldevtools_build|qtConfig(qml-interpreter) {
HEADERS += \
- $$PWD/qv4instr_moth_p.h \
- $$PWD/qv4isel_moth_p.h
+ $$PWD/qv4instr_moth_p.h
SOURCES += \
- $$PWD/qv4instr_moth.cpp \
- $$PWD/qv4isel_moth.cpp
+ $$PWD/qv4instr_moth.cpp
}
gcc {
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 02ff8947c4..128eb2c720 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -43,6 +43,7 @@
#include <private/qv4compileddata_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h>
+#include <private/qv4compilerscanfunctions_p.h>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <cmath>
@@ -61,7 +62,7 @@ QT_USE_NAMESPACE
static const quint32 emptyStringIndex = 0;
-#ifndef V4_BOOTSTRAP
+#if 0 //ndef V4_BOOTSTRAP
DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
#endif // V4_BOOTSTRAP
@@ -1067,15 +1068,18 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
} else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
binding->type = QV4::CompiledData::Binding::Type_Number;
binding->setNumberValueInternal(lit->value);
+ } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
+ if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
+ tryGeneratingTranslationBinding(base->name, call->arguments, binding);
+ // If it wasn't a translation binding, a normal script binding will be generated
+ // below.
+ }
} else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression;
- } else {
-
- if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
- if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
- binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->setNumberValueInternal(-lit->value);
- }
+ } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
+ if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
+ binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->setNumberValueInternal(-lit->value);
}
}
}
@@ -1097,6 +1101,121 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
}
}
+void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding)
+{
+ if (base == QLatin1String("qsTr")) {
+ QV4::CompiledData::TranslationData translationData;
+ translationData.number = -1;
+ translationData.commentIndex = 0; // empty string
+
+ if (!args || !args->expression)
+ return; // no arguments, stop
+
+ QStringRef translation;
+ if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) {
+ translation = arg1->value;
+ } else {
+ return; // first argument is not a string, stop
+ }
+
+ args = args->next;
+
+ if (args) {
+ QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression);
+ if (!arg2)
+ return; // second argument is not a string, stop
+ translationData.commentIndex = jsGenerator->registerString(arg2->value.toString());
+
+ args = args->next;
+ if (args) {
+ if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) {
+ translationData.number = int(arg3->value);
+ args = args->next;
+ } else {
+ return; // third argument is not a translation number, stop
+ }
+ }
+ }
+
+ if (args)
+ return; // too many arguments, stop
+
+ binding->type = QV4::CompiledData::Binding::Type_Translation;
+ binding->stringIndex = jsGenerator->registerString(translation.toString());
+ binding->value.translationData = translationData;
+ } else if (base == QLatin1String("qsTrId")) {
+ QV4::CompiledData::TranslationData translationData;
+ translationData.number = -1;
+ translationData.commentIndex = 0; // empty string, but unused
+
+ if (!args || !args->expression)
+ return; // no arguments, stop
+
+ QStringRef id;
+ if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) {
+ id = arg1->value;
+ } else {
+ return; // first argument is not a string, stop
+ }
+
+ args = args->next;
+
+ if (args) {
+ if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) {
+ translationData.number = int(arg3->value);
+ args = args->next;
+ } else {
+ return; // third argument is not a translation number, stop
+ }
+ }
+
+ if (args)
+ return; // too many arguments, stop
+
+ binding->type = QV4::CompiledData::Binding::Type_TranslationById;
+ binding->stringIndex = jsGenerator->registerString(id.toString());
+ binding->value.translationData = translationData;
+ } else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) {
+ if (!args || !args->expression)
+ return; // no arguments, stop
+
+ QStringRef str;
+ if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) {
+ str = arg1->value;
+ } else {
+ return; // first argument is not a string, stop
+ }
+
+ args = args->next;
+ if (args)
+ return; // too many arguments, stop
+
+ binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->stringIndex = jsGenerator->registerString(str.toString());
+ } else if (base == QLatin1String("QT_TRANSLATE_NOOP")) {
+ if (!args || !args->expression)
+ return; // no arguments, stop
+
+ args = args->next;
+ if (!args || !args->expression)
+ return; // no second arguments, stop
+
+ QStringRef str;
+ if (QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) {
+ str = arg2->value;
+ } else {
+ return; // first argument is not a string, stop
+ }
+
+ args = args->next;
+ if (args)
+ return; // too many arguments, stop
+
+ binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->stringIndex = jsGenerator->registerString(str.toString());
+ }
+}
+
void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
@@ -1649,9 +1768,10 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
return bindingPtr;
}
-JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine,
+JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
+ QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool)
- : QQmlJS::Codegen(/*strict mode*/false)
+ : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
, sourceCode(sourceCode)
, jsEngine(jsEngine)
, qmlRoot(qmlRoot)
@@ -1660,11 +1780,10 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR
, _disableAcceleratedLookups(false)
, _contextObject(0)
, _scopeObject(0)
- , _qmlContextTemp(-1)
- , _importedScriptsTemp(-1)
+ , _qmlContextSlot(-1)
+ , _importedScriptsSlot(-1)
{
_module = jsModule;
- _module->setFileName(fileName);
_fileNameIsUrl = true;
}
@@ -1684,8 +1803,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
{
QVector<int> runtimeFunctionIndices(functions.size());
- ScanFunctions scan(this, sourceCode, GlobalCode);
- scan.enterEnvironment(0, QmlBinding);
+ QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode);
+ scan.enterEnvironment(0, QV4::Compiler::QmlBinding);
scan.enterQmlScope(qmlRoot, QStringLiteral("context scope"));
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
@@ -1694,7 +1813,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
if (function)
scan.enterQmlFunction(function);
else
- scan.enterEnvironment(f.node, QmlBinding);
+ scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding);
scan(function ? function->body : f.node);
scan.leaveEnvironment();
@@ -1702,8 +1821,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
scan.leaveEnvironment();
scan.leaveEnvironment();
- _variableEnvironment = 0;
- _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0));
+ _context = 0;
for (int i = 0; i < functions.count(); ++i) {
const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
@@ -1745,35 +1863,36 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
runtimeFunctionIndices[i] = idx;
}
- qDeleteAll(_envMap);
- _envMap.clear();
return runtimeFunctionIndices;
}
+int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body)
+{
+ int qmlContextTemp = -1;
+ int importedScriptsTemp = -1;
+ qSwap(_qmlContextSlot, qmlContextTemp);
+ qSwap(_importedScriptsSlot, importedScriptsTemp);
+
+ int result = Codegen::defineFunction(name, ast, formals, body);
+
+ qSwap(_importedScriptsSlot, importedScriptsTemp);
+ qSwap(_qmlContextSlot, qmlContextTemp);
+
+ return result;
+}
+
#ifndef V4_BOOTSTRAP
-QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup)
+QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name)
{
- if (propertyExistsButForceNameLookup)
- *propertyExistsButForceNameLookup = false;
QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0);
// Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (pd && pd->isFunction()) {
- if (propertyExistsButForceNameLookup)
- *propertyExistsButForceNameLookup = true;
- pd = 0;
- }
+ if (!pd || pd->isFunction())
+ return 0;
- if (pd && !cache->isAllowedInRevision(pd))
- pd = 0;
+ if (!cache->isAllowedInRevision(pd))
+ return 0;
- // Return a copy allocated from our memory pool. Property data pointers can change
- // otherwise when the QQmlPropertyCache changes later in the QML type compilation process.
- if (pd) {
- QQmlPropertyData *copy = pd;
- pd = _function->New<QQmlPropertyData>();
- *pd = *copy;
- }
return pd;
}
@@ -1784,7 +1903,9 @@ enum MetaObjectResolverFlags {
ResolveTypeInformationOnly = 0x8
};
+#if 0
static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
+
static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index);
static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
@@ -2018,34 +2139,40 @@ static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver,
resolver->qmlType = qmlType;
resolver->flags = index;
}
+#endif
#endif // V4_BOOTSTRAP
void JSCodeGen::beginFunctionBodyHook()
{
- _qmlContextTemp = _block->newTemp();
- _importedScriptsTemp = _block->newTemp();
+ _qmlContextSlot = bytecodeGenerator->newRegister();
+ _importedScriptsSlot = bytecodeGenerator->newRegister();
#ifndef V4_BOOTSTRAP
- QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp);
+ Instruction::LoadQmlContext load;
+ load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
+ bytecodeGenerator->addInstruction(load);
+
+#if 0
temp->type = QV4::IR::QObjectType;
temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
initMetaObjectResolver(temp->memberResolver, _scopeObject);
auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0);
name->type = temp->type;
- move(temp, name);
+#endif
- move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0));
+ Instruction::LoadQmlImportedScripts loadScripts;
+ loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot();
+ bytecodeGenerator->addInstruction(loadScripts);
#endif
}
-QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col)
+QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
{
- Q_UNUSED(line)
- Q_UNUSED(col)
#ifndef V4_BOOTSTRAP
if (_disableAcceleratedLookups)
- return 0;
+ return Reference();
+
// Implement QML lookup semantics in the current file context.
//
// Note: We do not check if properties of the qml scope object or context object
@@ -2060,20 +2187,16 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_function->isQmlBinding)
- _function->idObjectDependencies.insert(mapping.idIndex);
-
- QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex);
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- _block->MOVE(result, s);
- result = _block->TEMP(result->index);
- if (mapping.type) {
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initMetaObjectResolver(result->memberResolver, mapping.type);
- result->memberResolver->flags |= AllPropertiesAreFinal;
- }
- result->isReadOnly = true; // don't allow use as lvalue
+ if (_context->compilationMode == QV4::Compiler::QmlBinding)
+ _context->idObjectDependencies.insert(mapping.idIndex);
+
+ Instruction::LoadIdObject load;
+ load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
+ load.index = mapping.idIndex;
+
+ Reference result = Reference::fromAccumulator(this);
+ bytecodeGenerator->addInstruction(load);
+ result.isReadonly = true;
return result;
}
}
@@ -2082,69 +2205,47 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
QQmlTypeNameCache::Result r = imports->query(name);
if (r.isValid()) {
if (r.scriptIndex != -1) {
- return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp),
- _block->CONST(QV4::IR::SInt32Type, r.scriptIndex));
+ Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot);
+ return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex)));
} else if (r.type.isValid()) {
- QV4::IR::Name *typeName = _block->NAME(name, line, col);
- // Make sure the run-time loads this through the more efficient singleton getter.
- typeName->qmlSingleton = r.type.isCompositeSingleton();
- typeName->freeOfSideEffects = true;
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- _block->MOVE(result, typeName);
-
- result = _block->TEMP(result->index);
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initQmlTypeResolver(result->memberResolver, r.type);
- return result;
+ if (r.type.isCompositeSingleton()) {
+ Instruction::LoadQmlSingleton load;
+ load.name = registerString(name);
+ bytecodeGenerator->addInstruction(load);
+ return Reference::fromAccumulator(this);
+ }
+ return Reference::fromName(this, name);
} else {
Q_ASSERT(r.importNamespace);
- QV4::IR::Name *namespaceName = _block->NAME(name, line, col);
- namespaceName->freeOfSideEffects = true;
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initImportNamespaceResolver(result->memberResolver, imports, r.importNamespace);
-
- _block->MOVE(result, namespaceName);
- return _block->TEMP(result->index);
+ return Reference::fromName(this, name);
}
}
}
if (_scopeObject) {
- bool propertyExistsButForceNameLookup = false;
- QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup);
- if (propertyExistsButForceNameLookup)
- return 0;
- if (pd) {
- QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp);
- base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- base->memberResolver->owner = _function;
- initMetaObjectResolver(base->memberResolver, _scopeObject);
- return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject);
- }
+ QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
+ if (!data)
+ return Reference::fromName(this, name);
+ Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
+ bool captureRequired = !data->isConstant() && !data->isQmlBinding();
+ return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(),
+ captureRequired);
}
if (_contextObject) {
- bool propertyExistsButForceNameLookup = false;
- QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup);
- if (propertyExistsButForceNameLookup)
- return 0;
- if (pd) {
- QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp);
- base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- base->memberResolver->owner = _function;
- initMetaObjectResolver(base->memberResolver, _contextObject);
- return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject);
- }
+ QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name);
+ if (!data)
+ return Reference::fromName(this, name);
+ Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
+ bool captureRequired = !data->isConstant() && !data->isQmlBinding();
+ return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(),
+ captureRequired);
}
-
#else
Q_UNUSED(name)
#endif // V4_BOOTSTRAP
// fall back to name lookup at run-time.
- return 0;
+ return Reference();
}
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index a5b4815745..406c939998 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -453,7 +453,7 @@ struct Q_QML_PRIVATE_EXPORT Document
Document(bool debugMode);
QString code;
QQmlJS::Engine jsParserEngine;
- QV4::IR::Module jsModule;
+ QV4::Compiler::Module jsModule;
QList<const QV4::CompiledData::Import *> imports;
QList<Pragma*> pragmas;
QQmlJS::AST::UiProgram *program;
@@ -527,6 +527,7 @@ public:
const QQmlJS::AST::SourceLocation &last) const;
void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement);
+ void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
@@ -606,9 +607,9 @@ struct Q_QML_EXPORT PropertyResolver
};
#endif
-struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen
+struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
- JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule,
+ JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
const QV4::Compiler::StringTableGenerator *stringPool);
@@ -626,12 +627,17 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
+ int defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::SourceElements *body) override;
+
protected:
void beginFunctionBodyHook() override;
- QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col) override;
+ Reference fallbackNameLookup(const QString &name) override;
private:
- QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup = 0);
+ // returns nullptr if lookup needs to happen by name
+ QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name);
QString sourceCode;
QQmlJS::Engine *jsEngine; // needed for memory pool
@@ -643,8 +649,8 @@ private:
ObjectIdMapping _idObjects;
QQmlPropertyCache *_contextObject;
QQmlPropertyCache *_scopeObject;
- int _qmlContextTemp;
- int _importedScriptsTemp;
+ int _qmlContextSlot;
+ int _importedScriptsSlot;
};
struct Q_QML_PRIVATE_EXPORT IRLoader {
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 46f0c46b99..97ca597953 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,10 +44,9 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qv4ssa_p.h>
#include "qqmlpropertycachecreator_p.h"
-#include "qv4jssimplifier_p.h"
+//#include "qv4jssimplifier_p.h"
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -140,19 +139,15 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
sss.scan();
}
- QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable);
+ document->jsModule.fileName = typeData->finalUrlString();
+ QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable);
+ v4CodeGenerator.setUseFastLookups(false);
+ // ### v4CodeGenerator.setUseTypeInference(true);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
- QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator);
- pass.reduceTranslationBindings();
-
- QV4::ExecutionEngine *v4 = engine->v4engine();
- QScopedPointer<QV4::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &document->jsModule, &document->jsGenerator));
- isel->setUseFastLookups(false);
- isel->setUseTypeInference(true);
- document->javaScriptCompilationUnit = isel->compile(/*generated unit data*/false);
+ document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
}
// Generate QML compiled type data structures
@@ -208,11 +203,6 @@ int QQmlTypeCompiler::registerString(const QString &str)
return document->jsGenerator.registerString(str);
}
-QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const
-{
- return &document->jsModule;
-}
-
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
return document->javaScriptCompilationUnit->data;
@@ -501,7 +491,9 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
- functionDeclaration->functionToken = foe->node->firstSourceLocation();
+ functionDeclaration->lbraceToken = functionDeclaration->functionToken
+ = foe->node->firstSourceLocation();
+ functionDeclaration->rbraceToken = foe->node->lastSourceLocation();
}
foe->node = functionDeclaration;
binding->propertyNameIndex = compiler->registerString(propertyName);
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 4878a90641..d905b956c7 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -111,8 +111,6 @@ public:
int registerString(const QString &str);
- QV4::IR::Module *jsIRModule() const;
-
const QV4::CompiledData::Unit *qmlUnit() const;
QUrl url() const { return typeData->finalUrl(); }
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
new file mode 100644
index 0000000000..05bbf25292
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qv4compileddata_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+void BytecodeGenerator::setLocation(const QQmlJS::AST::SourceLocation &loc)
+{
+ currentLine = static_cast<int>(loc.startLine);
+}
+
+int BytecodeGenerator::newRegister()
+{
+ int t = currentReg++;
+ if (regCount < currentReg)
+ regCount = currentReg;
+ return t;
+}
+
+int BytecodeGenerator::newRegisterArray(int n)
+{
+ int t = currentReg;
+ currentReg += n;
+ if (regCount < currentReg)
+ regCount = currentReg;
+ return t;
+}
+
+void BytecodeGenerator::packInstruction(I &i)
+{
+ uchar type = *reinterpret_cast<uchar *>(i.packed);
+ Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS());
+ if (type >= MOTH_NUM_INSTRUCTIONS())
+ type -= MOTH_NUM_INSTRUCTIONS();
+ int instructionsAsInts[sizeof(Instr)/sizeof(int)];
+ int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
+ memcpy(instructionsAsInts, i.packed + 1, nMembers*sizeof(int));
+ enum {
+ Normal,
+ Wide
+ } width = Normal;
+ for (int n = 0; n < nMembers; ++n) {
+ if (width == Normal && (static_cast<char>(instructionsAsInts[n]) != instructionsAsInts[n]))
+ width = Wide;
+ }
+ char *code = i.packed;
+ switch (width) {
+ case Normal:
+ *reinterpret_cast<uchar *>(code) = type;
+ ++code;
+ for (int n = 0; n < nMembers; ++n) {
+ char v = static_cast<char>(instructionsAsInts[n]);
+ memcpy(code, &v, 1);
+ code += 1;
+ }
+ i.size = code - i.packed;
+ if (i.offsetForJump != -1)
+ i.offsetForJump = i.size - 1;
+ break;
+ case Wide:
+ // nothing to do
+ break;
+ }
+}
+
+void BytecodeGenerator::adjustJumpOffsets()
+{
+ for (int index = 0; index < instructions.size(); ++index) {
+ auto &i = instructions[index];
+ if (i.offsetForJump == -1) // no jump
+ continue;
+ Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
+ const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel));
+ char *c = i.packed + i.offsetForJump;
+ int jumpOffset = linkedInstruction.position - (i.position + i.size);
+// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
+// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
+ uchar type = *reinterpret_cast<const uchar *>(i.packed);
+ if (type >= MOTH_NUM_INSTRUCTIONS()) {
+ Q_ASSERT(i.offsetForJump == i.size - 4);
+ memcpy(c, &jumpOffset, sizeof(int));
+ } else {
+ Q_ASSERT(i.offsetForJump == i.size - 1);
+ char o = jumpOffset;
+ Q_ASSERT(o == jumpOffset);
+ *c = o;
+ }
+ }
+}
+
+void BytecodeGenerator::compressInstructions()
+{
+ // first round: compress all non jump instructions
+ int position = 0;
+ for (auto &i : instructions) {
+ i.position = position;
+ if (i.offsetForJump == -1)
+ packInstruction(i);
+ position += i.size;
+ }
+
+ adjustJumpOffsets();
+
+ // compress all jumps
+ position = 0;
+ for (auto &i : instructions) {
+ i.position = position;
+ if (i.offsetForJump != -1)
+ packInstruction(i);
+ position += i.size;
+ }
+
+ // adjust once again, as the packing above could have changed offsets
+ adjustJumpOffsets();
+}
+
+void BytecodeGenerator::finalize(Compiler::Context *context)
+{
+ compressInstructions();
+
+ // collect content and line numbers
+ QByteArray code;
+ QVector<CompiledData::CodeOffsetToLine> lineNumbers;
+ currentLine = -1;
+ Q_UNUSED(startLine);
+ for (const auto &i : qAsConst(instructions)) {
+ if (i.line != currentLine) {
+ currentLine = i.line;
+ CompiledData::CodeOffsetToLine entry;
+ entry.codeOffset = code.size();
+ entry.line = currentLine;
+ lineNumbers.append(entry);
+ }
+ code.append(i.packed, i.size);
+ }
+
+ context->code = code;
+ context->lineNumberMapping = lineNumbers;
+}
+
+int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
+ if (debugMode && type != Instr::Type::Debug) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
+ if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
+ addInstruction(Instruction::Debug());
+ } else if (type == Instr::Type::Ret) {
+ currentLine = -currentLine;
+ addInstruction(Instruction::Debug());
+ currentLine = -currentLine;
+ }
+QT_WARNING_POP
+ }
+
+ const int pos = instructions.size();
+
+ int s = Moth::InstrInfo::argumentCount[static_cast<int>(type)]*sizeof(int);
+ if (offsetOfOffset != -1)
+ offsetOfOffset += 1;
+ I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" };
+ char *code = instr.packed;
+ *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type));
+ ++code;
+ Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256);
+ memcpy(code, &i, s);
+ instructions.append(instr);
+
+ return pos;
+}
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
new file mode 100644
index 0000000000..3d516da922
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4BYTECODEGENERATOR_P_H
+#define QV4BYTECODEGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qv4instr_moth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace AST {
+class SourceLocation;
+}
+}
+
+namespace QV4 {
+namespace Moth {
+
+class BytecodeGenerator {
+public:
+ BytecodeGenerator(int line, bool debug)
+ : startLine(line), debugMode(debug) {}
+
+ struct Label {
+ enum LinkMode {
+ LinkNow,
+ LinkLater
+ };
+ Label() = default;
+ Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
+ : generator(generator),
+ index(generator->labels.size()) {
+ generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1);
+ }
+ static Label returnLabel() {
+ Label l;
+ l.index = INT_MAX;
+ return l;
+ }
+ bool isReturn() const {
+ return index == INT_MAX;
+ }
+
+ void link() {
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(generator->labels[index] == -1);
+ generator->labels[index] = generator->instructions.size();
+ }
+
+ BytecodeGenerator *generator = 0;
+ int index = -1;
+ };
+
+ struct Jump {
+ Jump(BytecodeGenerator *generator, int instruction)
+ : generator(generator),
+ index(instruction)
+ {}
+ ~Jump() {
+ Q_ASSERT(generator->instructions[index].linkedLabel != -1);
+ }
+
+
+ BytecodeGenerator *generator;
+ int index;
+
+ void link() {
+ link(generator->label());
+ }
+ void link(Label l) {
+ Q_ASSERT(l.index >= 0);
+ Q_ASSERT(generator->instructions[index].linkedLabel == -1);
+ generator->instructions[index].linkedLabel = l.index;
+ }
+ };
+
+ struct ExceptionHandler : public Label {
+ ExceptionHandler(BytecodeGenerator *generator)
+ : Label(generator, LinkLater)
+ {
+ }
+ ~ExceptionHandler()
+ {
+ Q_ASSERT(generator->currentExceptionHandler != this);
+ }
+ };
+
+ Label label() {
+ return Label(this, Label::LinkNow);
+ }
+
+ Label newLabel() {
+ return Label(this, Label::LinkLater);
+ }
+
+ ExceptionHandler newExceptionHandler() {
+ return ExceptionHandler(this);
+ }
+
+ template<int InstrT>
+ void addInstruction(const InstrData<InstrT> &data)
+ {
+ Instr genericInstr;
+ InstrMeta<InstrT>::setData(genericInstr, data);
+ addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr);
+ }
+
+ Q_REQUIRED_RESULT Jump jump()
+ {
+ Instruction::Jump data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpTrue()
+ {
+ Instruction::JumpTrue data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpFalse()
+ {
+ Instruction::JumpFalse data;
+ return addJumpInstruction(data);
+ }
+
+ void jumpStrictEqual(const StackSlot &lhs, const Label &target)
+ {
+ Instruction::CmpStrictEqual cmp;
+ cmp.lhs = lhs;
+ addInstruction(cmp);
+ addJumpInstruction(Instruction::JumpTrue()).link(target);
+ }
+
+ void jumpStrictNotEqual(const StackSlot &lhs, const Label &target)
+ {
+ Instruction::CmpStrictNotEqual cmp;
+ cmp.lhs = lhs;
+ addInstruction(cmp);
+ addJumpInstruction(Instruction::JumpTrue()).link(target);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
+ {
+ Instruction::JumpStrictEqualStackSlotInt data;
+ data.lhs = lhs;
+ data.rhs = rhs;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
+ {
+ Instruction::JumpStrictNotEqualStackSlotInt data;
+ data.lhs = lhs;
+ data.rhs = rhs;
+ return addJumpInstruction(data);
+ }
+
+ void setExceptionHandler(ExceptionHandler *handler)
+ {
+ currentExceptionHandler = handler;
+ Instruction::SetExceptionHandler data;
+ data.offset = 0;
+ if (!handler)
+ addInstruction(data);
+ else
+ addJumpInstruction(data).link(*handler);
+ }
+
+ void setLocation(const QQmlJS::AST::SourceLocation &loc);
+
+ ExceptionHandler *exceptionHandler() const {
+ return currentExceptionHandler;
+ }
+
+ int newRegister();
+ int newRegisterArray(int n);
+ int registerCount() const { return regCount; }
+
+ void finalize(Compiler::Context *context);
+
+ template<int InstrT>
+ Jump addJumpInstruction(const InstrData<InstrT> &data)
+ {
+ Instr genericInstr;
+ InstrMeta<InstrT>::setData(genericInstr, data);
+ return Jump(this, addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr, offsetof(InstrData<InstrT>, offset)));
+ }
+
+ void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel)
+ {
+ if (jumpOnFalse)
+ addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel);
+ else
+ addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
+ }
+
+private:
+ friend struct Jump;
+ friend struct Label;
+ friend struct ExceptionHandler;
+
+ int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1);
+
+ struct I {
+ Moth::Instr::Type type;
+ short size;
+ uint position;
+ int line;
+ int offsetForJump;
+ int linkedLabel;
+ char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix
+ };
+
+ void compressInstructions();
+ void packInstruction(I &i);
+ void adjustJumpOffsets();
+
+ QVector<I> instructions;
+ QVector<int> labels;
+ ExceptionHandler *currentExceptionHandler;
+ int regCount = 0;
+public:
+ int currentReg = 0;
+private:
+ int startLine = 0;
+ int currentLine = 0;
+ bool debugMode = false;
+};
+
+}
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 3fff29bce0..b9d223bab0 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -39,7 +39,6 @@
#include "qv4codegen_p.h"
#include "qv4util_p.h"
-#include "qv4engine_p.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
@@ -51,10 +50,10 @@
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
-
-#ifndef V4_BOOTSTRAP
-#include <qv4context_p.h>
-#endif
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4compilercontrolflow_p.h>
+#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4compilerscanfunctions_p.h>
#include <cmath>
#include <iostream>
@@ -63,37 +62,13 @@
#undef CONST
#endif
+QT_USE_NAMESPACE
using namespace QV4;
-using namespace QQmlJS;
-using namespace AST;
-
-static inline void setLocation(IR::Stmt *s, const SourceLocation &loc)
-{
- if (s && loc.isValid())
- s->location = loc;
-}
-
-static bool cjumpCanHandle(IR::AluOp op)
-{
- switch (op) {
- case IR::OpIn:
- case IR::OpInstanceof:
- case IR::OpEqual:
- case IR::OpNotEqual:
- case IR::OpGe:
- case IR::OpGt:
- case IR::OpLe:
- case IR::OpLt:
- case IR::OpStrictEqual:
- case IR::OpStrictNotEqual:
- return true;
- default:
- return false;
- }
-}
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
-static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
- const SourceLocation &fallback)
+static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator,
+ const Statement *body, const SourceLocation &fallback)
{
switch (body->kind) {
// Statements where we might never execute the last line.
@@ -105,672 +80,170 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
case Statement::Kind_LocalForEachStatement:
case Statement::Kind_LocalForStatement:
case Statement::Kind_WhileStatement:
- setLocation(s, fallback);
+ bytecodeGenerator->setLocation(fallback);
break;
default:
- setLocation(s, body->lastSourceLocation());
+ bytecodeGenerator->setLocation(body->lastSourceLocation());
break;
}
}
-static inline bool isSimpleExpr(IR::Expr *e)
-{
- switch (e->exprKind) {
- case IR::Expr::TempExpr:
- case IR::Expr::ArgLocalExpr:
- case IR::Expr::ConstExpr:
- return true;
- default:
- return false;
- }
-}
-
-Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
- : _cg(cg)
- , _sourceCode(sourceCode)
- , _variableEnvironment(0)
- , _allowFuncDecls(true)
- , defaultProgramMode(defaultProgramMode)
-{
-}
-
-void Codegen::ScanFunctions::operator()(Node *node)
-{
- if (node)
- node->accept(this);
-}
-
-void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
-{
- Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode);
- if (!e->isStrict)
- e->isStrict = _cg->_strictMode;
- _envStack.append(e);
- _variableEnvironment = e;
-}
-
-void Codegen::ScanFunctions::leaveEnvironment()
-{
- _envStack.pop();
- _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top();
-}
-
-void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast)
-{
- for (SourceElements *it = ast; it; it = it->next) {
- if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
- if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
- // Use the source code, because the StringLiteral's
- // value might have escape sequences in it, which is not
- // allowed.
- if (strLit->literalToken.length < 2)
- continue;
- QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
- if (str == QLatin1String("use strict")) {
- _variableEnvironment->isStrict = true;
- } else {
- // TODO: give a warning.
- }
- continue;
- }
- }
- }
-
- break;
- }
-}
-
-void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
-{
- if (_variableEnvironment->isStrict) {
- if (name == QLatin1String("implements")
- || name == QLatin1String("interface")
- || name == QLatin1String("let")
- || name == QLatin1String("package")
- || name == QLatin1String("private")
- || name == QLatin1String("protected")
- || name == QLatin1String("public")
- || name == QLatin1String("static")
- || name == QLatin1String("yield")) {
- _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word"));
- }
- }
-}
-void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters)
-{
- while (parameters) {
- if (parameters->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- parameters = parameters->next;
- }
-}
-
-bool Codegen::ScanFunctions::visit(Program *ast)
-{
- enterEnvironment(ast, defaultProgramMode);
- checkDirectivePrologue(ast->elements);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(Program *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(CallExpression *ast)
-{
- if (! _variableEnvironment->hasDirectEval) {
- if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
- if (id->name == QLatin1String("eval")) {
- if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown)
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed;
- _variableEnvironment->hasDirectEval = true;
- }
- }
- }
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(NewMemberExpression *ast)
-{
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(ArrayLiteral *ast)
-{
- int index = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next)
- ++index;
- ++index;
- }
- if (ast->elision) {
- for (Elision *elision = ast->elision->next; elision; elision = elision->next)
- ++index;
- }
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(VariableDeclaration *ast)
-{
- if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(ast->name, ast->identifierToken);
- if (ast->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
- return false;
- }
- QString name = ast->name.toString();
- const Environment::Member *m = 0;
- if (_variableEnvironment->memberInfo(name, &m)) {
- if (m->isLexicallyScoped() || ast->isLexicallyScoped()) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
- return false;
- }
- }
- _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(IdentifierExpression *ast)
-{
- checkName(ast->name, ast->identifierToken);
- if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed;
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(ExpressionStatement *ast)
-{
- if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
- if (!_allowFuncDecls)
- _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
-
- enterFunction(expr, /*enterName*/ true);
- Node::accept(expr->formals, this);
- Node::accept(expr->body, this);
- leaveEnvironment();
- return false;
- } else {
- SourceLocation firstToken = ast->firstSourceLocation();
- if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) {
- _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token"));
- }
- }
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(FunctionExpression *ast)
-{
- enterFunction(ast, /*enterName*/ false);
- return true;
-}
-
-void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression)
-{
- if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
- enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression);
-}
-
-void Codegen::ScanFunctions::endVisit(FunctionExpression *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(ObjectLiteral *ast)
-{
- int argc = 0;
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString key = it->assignment->name->asString();
- if (QV4::String::toArrayIndex(key) != UINT_MAX)
- ++argc;
- ++argc;
- if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
- ++argc;
- }
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- Node::accept(ast->properties, this);
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast)
-{
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast)
-{
- enterFunction(ast, /*enterName*/ true, /*isExpression */false);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(FunctionDeclaration *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(WithStatement *ast)
-{
- if (_variableEnvironment->isStrict) {
- _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
- return false;
- }
-
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) {
- {
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
- }
- Node::accept(ast->expression, this);
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ForStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->condition, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(LocalForStatement *ast) {
- Node::accept(ast->declarations, this);
- Node::accept(ast->condition, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ForEachStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) {
- Node::accept(ast->declaration, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ThisExpression *)
-{
- _variableEnvironment->usesThis = true;
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(Block *ast) {
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls);
- Node::accept(ast->statements, this);
- return false;
-}
-
-void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression)
-{
- bool wasStrict = false;
- if (_variableEnvironment) {
- _variableEnvironment->hasNestedFunctions = true;
- // The identifier of a function expression cannot be referenced from the enclosing environment.
- if (expr)
- _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr);
- if (name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- wasStrict = _variableEnvironment->isStrict;
- }
-
- enterEnvironment(ast, FunctionCode);
- checkForArguments(formals);
-
- _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty();
- _variableEnvironment->formals = formals;
-
- if (body)
- checkDirectivePrologue(body->elements);
-
- if (wasStrict || _variableEnvironment->isStrict) {
- QStringList args;
- for (FormalParameterList *it = formals; it; it = it->next) {
- QString arg = it->name.toString();
- if (args.contains(arg)) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
- return;
- }
- if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
- return;
- }
- args += arg;
- }
- }
-}
-
-
-Codegen::Codegen(bool strict)
+Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(0)
- , _function(0)
- , _block(0)
- , _exitBlock(0)
, _returnAddress(0)
- , _variableEnvironment(0)
- , _loop(0)
+ , _context(0)
, _labelledStatement(0)
- , _scopeAndFinally(0)
+ , jsUnitGenerator(jsUnitGenerator)
, _strictMode(strict)
, _fileNameIsUrl(false)
, hasError(false)
{
+ jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
}
void Codegen::generateFromProgram(const QString &fileName,
const QString &sourceCode,
Program *node,
- QV4::IR::Module *module,
- CompilationMode mode,
- const QStringList &inheritedLocals)
+ Module *module,
+ CompilationMode mode)
{
Q_ASSERT(node);
_module = module;
- _variableEnvironment = 0;
+ _context = 0;
- _module->setFileName(fileName);
+ _module->fileName = fileName;
ScanFunctions scan(this, sourceCode, mode);
scan(node);
- defineFunction(QStringLiteral("%entry"), node, 0, node->elements, inheritedLocals);
- qDeleteAll(_envMap);
- _envMap.clear();
-}
-
-void Codegen::generateFromFunctionExpression(const QString &fileName,
- const QString &sourceCode,
- AST::FunctionExpression *ast,
- QV4::IR::Module *module)
-{
- _module = module;
- _module->setFileName(fileName);
- _variableEnvironment = 0;
-
- ScanFunctions scan(this, sourceCode, GlobalCode);
- // fake a global environment
- scan.enterEnvironment(0, FunctionCode);
- scan(ast);
- scan.leaveEnvironment();
-
- defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
-
- qDeleteAll(_envMap);
- _envMap.clear();
-}
-
-
-void Codegen::enterEnvironment(Node *node)
-{
- _variableEnvironment = _envMap.value(node);
- Q_ASSERT(_variableEnvironment);
-}
-
-void Codegen::leaveEnvironment()
-{
- Q_ASSERT(_variableEnvironment);
- _variableEnvironment = _variableEnvironment->parent;
-}
-
-void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock)
-{
- _loop = new Loop(node, breakBlock, continueBlock, _loop);
- _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement
- _loop->scopeAndFinally = _scopeAndFinally;
- _labelledStatement = 0;
-}
-
-void Codegen::leaveLoop()
-{
- Loop *current = _loop;
- _loop = _loop->parent;
- delete current;
-}
-
-IR::Expr *Codegen::member(IR::Expr *base, const QString *name)
-{
- if (hasError)
- return 0;
-
- if (base->asTemp() || base->asArgLocal())
- return _block->MEMBER(base, name);
- else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- return _block->MEMBER(_block->TEMP(t), name);
- }
+ defineFunction(QStringLiteral("%entry"), node, 0, node->elements);
}
-IR::Expr *Codegen::argument(IR::Expr *expr)
+void Codegen::enterContext(Node *node)
{
- if (expr && !expr->asTemp()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- return expr;
+ _context = _module->contextMap.value(node);
+ Q_ASSERT(_context);
}
-// keeps references alive, converts other expressions to temps
-IR::Expr *Codegen::reference(IR::Expr *expr)
+int Codegen::leaveContext()
{
- if (hasError)
- return 0;
-
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- return expr;
+ Q_ASSERT(_context);
+ Q_ASSERT(!_context->controlFlow);
+ int functionIndex = _context->functionIndex;
+ _context = _context->parent;
+ return functionIndex;
}
-IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc)
+Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
- return 0;
-
- Q_ASSERT(op != IR::OpIncrement);
- Q_ASSERT(op != IR::OpDecrement);
+ return _expr.result();
- if (IR::Const *c = expr->asConst()) {
- if (c->type == IR::NumberType) {
+#ifndef V4_BOOTSTRAP
+ if (expr.isConst()) {
+ auto v = Value::fromReturnedValue(expr.constant);
+ if (v.isNumber()) {
switch (op) {
- case IR::OpNot:
- return _block->CONST(IR::BoolType, !c->value);
- case IR::OpUMinus:
- return _block->CONST(IR::NumberType, -c->value);
- case IR::OpUPlus:
+ case Not:
+ return Reference::fromConst(this, Encode(!v.toBoolean()));
+ case UMinus:
+ return Reference::fromConst(this, Runtime::method_uMinus(v));
+ case UPlus:
return expr;
- case IR::OpCompl:
- return _block->CONST(IR::NumberType, ~QV4::Primitive::toInt32(c->value));
- case IR::OpIncrement:
- return _block->CONST(IR::NumberType, c->value + 1);
- case IR::OpDecrement:
- return _block->CONST(IR::NumberType, c->value - 1);
+ case Compl:
+ return Reference::fromConst(this, Encode((int)~v.toInt32()));
default:
break;
}
}
}
+#endif // V4_BOOTSTRAP
- TempScope scope(_function);
- if (isSimpleExpr(expr))
- return _block->UNOP(op, expr);
-
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), expr), loc);
- expr = _block->TEMP(t);
- return _block->UNOP(op, expr);
-}
-
-IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AST::SourceLocation &loc)
-{
- if (hasError)
- return 0;
-
- TempScope scope(_function);
-
- if (IR::Const *c1 = left->asConst()) {
- if (IR::Const *c2 = right->asConst()) {
- if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) {
- switch (op) {
- case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value);
- case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0);
- case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value));
- case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value));
- case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value));
- case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value);
- case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
- case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
- case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
- case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
- case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value);
- case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value);
- case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value);
- case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value);
- case IR::OpLShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) << (QV4::Primitive::toUInt32(c2->value) & 0x1f));
- case IR::OpMod: return _block->CONST(IR::NumberType, std::fmod(c1->value, c2->value));
- case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value);
- case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value);
- case IR::OpRShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f));
- case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value);
- case IR::OpURShift: return _block->CONST(IR::NumberType,QV4::Primitive::toUInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f));
-
- case IR::OpInstanceof:
- case IR::OpIn:
- break;
-
- case IR::OpIfTrue: // unary ops
- case IR::OpNot:
- case IR::OpUMinus:
- case IR::OpUPlus:
- case IR::OpCompl:
- case IR::OpIncrement:
- case IR::OpDecrement:
- case IR::OpInvalid:
- break;
- }
- }
+ switch (op) {
+ case UMinus: {
+ expr.loadInAccumulator();
+ Instruction::UMinus uminus;
+ bytecodeGenerator->addInstruction(uminus);
+ return Reference::fromAccumulator(this);
+ }
+ case UPlus: {
+ expr.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ return Reference::fromAccumulator(this);
+ }
+ case Not: {
+ expr.loadInAccumulator();
+ Instruction::UNot unot;
+ bytecodeGenerator->addInstruction(unot);
+ return Reference::fromAccumulator(this);
+ }
+ case Compl: {
+ expr.loadInAccumulator();
+ Instruction::UCompl ucompl;
+ bytecodeGenerator->addInstruction(ucompl);
+ return Reference::fromAccumulator(this);
+ }
+ case PostIncrement:
+ if (!_expr.accept(nx) || requiresReturnValue) {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
+ Instruction::Increment inc;
+ bytecodeGenerator->addInstruction(inc);
+ e.storeConsumeAccumulator();
+ return originalValue;
+ } else {
+ // intentionally fall-through: the result is never used, so it's equivalent to
+ // "expr += 1", which is what a pre-increment does as well.
}
- } else if (op == IR::OpAdd) {
- if (IR::String *s1 = left->asString()) {
- if (IR::String *s2 = right->asString()) {
- return _block->STRING(_function->newString(*s1->value + *s2->value));
- }
+ case PreIncrement: {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::Increment inc;
+ bytecodeGenerator->addInstruction(inc);
+ if (_expr.accept(nx))
+ return e.storeConsumeAccumulator();
+ else
+ return e.storeRetainAccumulator();
+ }
+ case PostDecrement:
+ if (!_expr.accept(nx) || requiresReturnValue) {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
+ Instruction::Decrement dec;
+ bytecodeGenerator->addInstruction(dec);
+ e.storeConsumeAccumulator();
+ return originalValue;
+ } else {
+ // intentionally fall-through: the result is never used, so it's equivalent to
+ // "expr -= 1", which is what a pre-decrement does as well.
}
+ case PreDecrement: {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::Decrement dec;
+ bytecodeGenerator->addInstruction(dec);
+ if (_expr.accept(nx))
+ return e.storeConsumeAccumulator();
+ else
+ return e.storeRetainAccumulator();
}
-
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), loc);
- left = _block->TEMP(t);
- }
-
- if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), right), loc);
- right = _block->TEMP(t);
- }
-
- Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst());
- Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst());
-
- return _block->BINOP(op, left, right);
-}
-
-IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args)
-{
- if (hasError)
- return 0;
- base = reference(base);
- return _block->CALL(base, args);
-}
-
-IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
-{
- if (hasError)
- return 0;
-
- Q_ASSERT(target->isLValue());
-
- if (op != IR::OpInvalid) {
- return move(target, binop(op, target, source));
- }
-
- TempScope scope(_function);
-
- if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) {
- unsigned t = _block->newTemp();
- _block->MOVE(_block->TEMP(t), source);
- source = _block->TEMP(t);
- }
- if (source->asConst() && !target->asTemp() && !target->asArgLocal()) {
- unsigned t = _block->newTemp();
- _block->MOVE(_block->TEMP(t), source);
- source = _block->TEMP(t);
}
- return _block->MOVE(target, source);
+ Q_UNREACHABLE();
}
-IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
+void Codegen::addCJump()
{
- if (hasError)
- return 0;
-
- TempScope scope(_function);
-
- if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), cond);
- cond = _block->TEMP(t);
- }
- return _block->CJUMP(cond, iftrue, iffalse);
+ bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
+ _expr.iftrue(), _expr.iffalse());
}
void Codegen::accept(Node *node)
@@ -784,52 +257,71 @@ void Codegen::accept(Node *node)
void Codegen::statement(Statement *ast)
{
- TempScope scope(_function);
+ RegisterScope scope(this);
+
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
- _block->nextLocation = ast->firstSourceLocation();
+ VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
+ qSwap(_volataleMemoryLocations, vLocs);
accept(ast);
+ qSwap(_volataleMemoryLocations, vLocs);
}
void Codegen::statement(ExpressionNode *ast)
{
- TempScope scope(_function);
+ RegisterScope scope(this);
if (! ast) {
return;
} else {
Result r(nx);
qSwap(_expr, r);
+ VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
+ qSwap(_volataleMemoryLocations, vLocs);
+
accept(ast);
+
+ qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_expr, r);
+
if (hasError)
return;
- qSwap(_expr, r);
- if (r.format == ex) {
- if (r->asCall()) {
- _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..))
- } else if (r->asTemp() || r->asArgLocal()) {
- // there is nothing to do
- } else {
- unsigned t = _block->newTemp();
- move(_block->TEMP(t), *r);
- }
- }
+ if (r.result().loadTriggersSideEffect())
+ r.result().loadInAccumulator(); // triggers side effects
}
}
-void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
+void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition)
{
- if (ast) {
- Result r(iftrue, iffalse);
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
- if (r.format == ex) {
- setLocation(cjump(*r, r.iftrue, r.iffalse), ast->firstSourceLocation());
- }
+ if (hasError)
+ return;
+
+ if (!ast)
+ return;
+
+ Result r(iftrue, iffalse, trueBlockFollowsCondition);
+ qSwap(_expr, r);
+ accept(ast);
+ qSwap(_expr, r);
+
+ if (hasError)
+ return;
+
+ if (r.format() == ex) {
+ Q_ASSERT(iftrue == r.iftrue());
+ Q_ASSERT(iffalse == r.iffalse());
+ Q_ASSERT(r.result().isValid());
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ r.result().loadInAccumulator();
+ if (r.trueBlockFollowsCondition())
+ bytecodeGenerator->jumpFalse().link(*r.iffalse());
+ else
+ bytecodeGenerator->jumpTrue().link(*r.iftrue());
}
}
-Codegen::Result Codegen::expression(ExpressionNode *ast)
+Codegen::Reference Codegen::expression(ExpressionNode *ast)
{
Result r;
if (ast) {
@@ -837,7 +329,7 @@ Codegen::Result Codegen::expression(ExpressionNode *ast)
accept(ast);
qSwap(_expr, r);
}
- return r;
+ return r.result();
}
Codegen::Result Codegen::sourceElement(SourceElement *ast)
@@ -851,17 +343,6 @@ Codegen::Result Codegen::sourceElement(SourceElement *ast)
return r;
}
-Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast)
-{
- UiMember m;
- if (ast) {
- qSwap(_uiMember, m);
- accept(ast);
- qSwap(_uiMember, m);
- }
- return m;
-}
-
void Codegen::functionBody(FunctionBody *ast)
{
if (ast)
@@ -877,36 +358,58 @@ void Codegen::program(Program *ast)
void Codegen::sourceElements(SourceElements *ast)
{
+ bool _requiresReturnValue = false;
+ qSwap(_requiresReturnValue, requiresReturnValue);
for (SourceElements *it = ast; it; it = it->next) {
+ if (!it->next)
+ qSwap(_requiresReturnValue, requiresReturnValue);
sourceElement(it->element);
if (hasError)
return;
+ if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) {
+ if (AST::cast<ThrowStatement *>(sse->statement) ||
+ AST::cast<ReturnStatement *>(sse->statement))
+ return;
+ }
+ }
+}
+
+void Codegen::statementList(StatementList *ast)
+{
+ bool _requiresReturnValue = requiresReturnValue;
+ requiresReturnValue = false;
+ for (StatementList *it = ast; it; it = it->next) {
+ if (!it->next ||
+ it->next->statement->kind == Statement::Kind_BreakStatement ||
+ it->next->statement->kind == Statement::Kind_ContinueStatement ||
+ it->next->statement->kind == Statement::Kind_ReturnStatement)
+ requiresReturnValue = _requiresReturnValue;
+ statement(it->statement);
+ requiresReturnValue = false;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ // any code after those statements is unreachable
+ break;
}
+ requiresReturnValue = _requiresReturnValue;
}
void Codegen::variableDeclaration(VariableDeclaration *ast)
{
- IR::Expr *initializer = 0;
+ RegisterScope scope(this);
+
if (!ast->expression)
return;
- Result expr = expression(ast->expression);
+ Reference rhs = expression(ast->expression);
if (hasError)
return;
- Q_ASSERT(expr.code);
- initializer = *expr;
-
- IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine,
- ast->identifierToken.startColumn);
-
- if (lhs->asArgLocal()) {
- move(lhs, initializer);
- } else {
- TempScope scope(_function);
- int initialized = _block->newTemp();
- move(_block->TEMP(initialized), initializer);
- move(lhs, _block->TEMP(initialized));
- }
+ Reference lhs = referenceForName(ast->name.toString(), true);
+ //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const
+ rhs.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
}
void Codegen::variableDeclarationList(VariableDeclarationList *ast)
@@ -919,175 +422,175 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast)
bool Codegen::visit(ArgumentList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseBlock *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseClause *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseClauses *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Catch *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(DefaultClause *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(ElementList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Elision *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Finally *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(FormalParameterList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(FunctionBody *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Program *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(PropertyAssignmentList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(PropertyNameAndValue *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(PropertyGetterSetter *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(SourceElements *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(StatementList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiArrayMemberList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiImport *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiHeaderItemList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiPragma *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiObjectInitializer *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiObjectMemberList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiParameterList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiProgram *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiQualifiedId *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiQualifiedPragmaId *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(VariableDeclaration *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(VariableDeclarationList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
@@ -1106,57 +609,47 @@ bool Codegen::visit(ArrayLiteral *ast)
if (hasError)
return false;
- const unsigned t = _block->newTemp();
+ RegisterScope scope(this);
- TempScope scope(_function);
+ int argc = 0;
+ int args = -1;
+ auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ if (!arg) {
+ auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
+ (void) c.storeOnStack(temp);
+ } else {
+ RegisterScope scope(this);
+ (void) expression(arg).storeOnStack(temp);
+ }
+ ++argc;
+ };
- IR::ExprList *args = 0;
- IR::ExprList *current = 0;
for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next) {
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
- } else {
- current->next = arg;
- }
- current = arg;
- current->expr = _block->CONST(IR::MissingType, 0);
- }
- Result expr = expression(it->expression);
- if (hasError)
- return false;
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
- } else {
- current->next = arg;
- }
- current = arg;
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ push(0);
- IR::Expr *exp = *expr;
- if (exp->asTemp() || expr->asArgLocal() || exp->asConst()) {
- current->expr = exp;
- } else {
- unsigned value = _block->newTemp();
- move(_block->TEMP(value), exp);
- current->expr = _block->TEMP(value);
- }
+ push(it->expression);
+ if (hasError)
+ return false;
}
- for (Elision *elision = ast->elision; elision; elision = elision->next) {
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
- } else {
- current->next = arg;
- }
- current = arg;
- current->expr = _block->CONST(IR::MissingType, 0);
+ for (Elision *elision = ast->elision; elision; elision = elision->next)
+ push(0);
+
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
}
- move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args));
- _expr.code = _block->TEMP(t);
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ _expr.setResult(Reference::fromAccumulator(this));
+
return false;
}
@@ -1165,43 +658,41 @@ bool Codegen::visit(ArrayMemberExpression *ast)
if (hasError)
return false;
- IR::Expr *base = *expression(ast->base);
+ Reference base = expression(ast->base);
if (hasError)
return false;
- if (!isSimpleExpr(base)) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- base = _block->TEMP(t);
- }
-
- IR::Expr *index = *expression(ast->expression);
- if (hasError)
+ base = base.storeOnStack();
+ if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
+ QString s = str->value.toString();
+ uint arrayIndex = QV4::String::toArrayIndex(s);
+ if (arrayIndex == UINT_MAX) {
+ _expr.setResult(Reference::fromMember(base, str->value.toString()));
+ return false;
+ }
+ Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
+ _expr.setResult(Reference::fromSubscript(base, index));
return false;
- if (!isSimpleExpr(index)) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), index);
- index = _block->TEMP(t);
}
-
- _expr.code = _block->SUBSCRIPT(base, index);
+ Reference index = expression(ast->expression);
+ _expr.setResult(Reference::fromSubscript(base, index));
return false;
}
-static IR::AluOp baseOp(int op)
+static QSOperator::Op baseOp(int op)
{
switch ((QSOperator::Op) op) {
- case QSOperator::InplaceAnd: return IR::OpBitAnd;
- case QSOperator::InplaceSub: return IR::OpSub;
- case QSOperator::InplaceDiv: return IR::OpDiv;
- case QSOperator::InplaceAdd: return IR::OpAdd;
- case QSOperator::InplaceLeftShift: return IR::OpLShift;
- case QSOperator::InplaceMod: return IR::OpMod;
- case QSOperator::InplaceMul: return IR::OpMul;
- case QSOperator::InplaceOr: return IR::OpBitOr;
- case QSOperator::InplaceRightShift: return IR::OpRShift;
- case QSOperator::InplaceURightShift: return IR::OpURShift;
- case QSOperator::InplaceXor: return IR::OpBitXor;
- default: return IR::OpInvalid;
+ case QSOperator::InplaceAnd: return QSOperator::BitAnd;
+ case QSOperator::InplaceSub: return QSOperator::Sub;
+ case QSOperator::InplaceDiv: return QSOperator::Div;
+ case QSOperator::InplaceAdd: return QSOperator::Add;
+ case QSOperator::InplaceLeftShift: return QSOperator::LShift;
+ case QSOperator::InplaceMod: return QSOperator::Mod;
+ case QSOperator::InplaceMul: return QSOperator::Mul;
+ case QSOperator::InplaceOr: return QSOperator::BitOr;
+ case QSOperator::InplaceRightShift: return QSOperator::RShift;
+ case QSOperator::InplaceURightShift: return QSOperator::URShift;
+ case QSOperator::InplaceXor: return QSOperator::BitXor;
+ default: return QSOperator::Invalid;
}
}
@@ -1212,89 +703,89 @@ bool Codegen::visit(BinaryExpression *ast)
if (ast->op == QSOperator::And) {
if (_expr.accept(cx)) {
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- condition(ast->left, iftrue, _expr.iffalse);
- _block = iftrue;
- condition(ast->right, _expr.iftrue, _expr.iffalse);
+ auto iftrue = bytecodeGenerator->newLabel();
+ condition(ast->left, &iftrue, _expr.iffalse(), true);
+ iftrue.link();
+ condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
} else {
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
-
- const unsigned r = _block->newTemp();
+ auto iftrue = bytecodeGenerator->newLabel();
+ auto endif = bytecodeGenerator->newLabel();
- Result lhs = expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
- move(_block->TEMP(r), *lhs);
- setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken);
- _block = iftrue;
- Result rhs = expression(ast->right);
+ left.loadInAccumulator();
+
+ bytecodeGenerator->setLocation(ast->operatorToken);
+ bytecodeGenerator->jumpFalse().link(endif);
+ iftrue.link();
+
+ Reference right = expression(ast->right);
if (hasError)
return false;
- move(_block->TEMP(r), *rhs);
- _block->JUMP(endif);
+ right.loadInAccumulator();
- _expr.code = _block->TEMP(r);
- _block = endif;
+ endif.link();
+
+ _expr.setResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
if (_expr.accept(cx)) {
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- condition(ast->left, _expr.iftrue, iffalse);
- _block = iffalse;
- condition(ast->right, _expr.iftrue, _expr.iffalse);
+ auto iffalse = bytecodeGenerator->newLabel();
+ condition(ast->left, _expr.iftrue(), &iffalse, false);
+ iffalse.link();
+ condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
} else {
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ auto iffalse = bytecodeGenerator->newLabel();
+ auto endif = bytecodeGenerator->newLabel();
- const unsigned r = _block->newTemp();
- Result lhs = expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
- move(_block->TEMP(r), *lhs);
- setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken);
- _block = iffalse;
- Result rhs = expression(ast->right);
+ left.loadInAccumulator();
+
+ bytecodeGenerator->setLocation(ast->operatorToken);
+ bytecodeGenerator->jumpTrue().link(endif);
+ iffalse.link();
+
+ Reference right = expression(ast->right);
if (hasError)
return false;
- move(_block->TEMP(r), *rhs);
- _block->JUMP(endif);
+ right.loadInAccumulator();
+
+ endif.link();
- _block = endif;
- _expr.code = _block->TEMP(r);
+ _expr.setResult(Reference::fromAccumulator(this));
}
return false;
}
- IR::Expr* left = *expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
switch (ast->op) {
case QSOperator::Or:
case QSOperator::And:
+ Q_UNREACHABLE(); // handled separately above
break;
case QSOperator::Assign: {
+ if (!left.isLValue()) {
+ throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
+ return false;
+ }
+ left = left.asLValue();
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
return false;
- Result right = expression(ast->right);
+ expression(ast->right).loadInAccumulator();
if (hasError)
return false;
- if (!left->isLValue()) {
- throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
- return false;
- }
-
- if (_expr.accept(nx)) {
- move(left, *right);
- } else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), *right);
- move(left, _block->TEMP(t));
- _expr.code = _block->TEMP(t);
- }
+ if (_expr.accept(nx))
+ _expr.setResult(left.storeConsumeAccumulator());
+ else
+ _expr.setResult(left.storeRetainAccumulator());
break;
}
@@ -1311,23 +802,36 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::InplaceXor: {
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
return false;
- Result right = expression(ast->right);
- if (hasError)
- return false;
- if (!left->isLValue()) {
+
+ if (!left.isLValue()) {
throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue"));
return false;
}
+ left = left.asLValue();
+
+ Reference tempLeft = left.storeOnStack();
+ Reference right = expression(ast->right);
+
+ if (hasError)
+ return false;
+
+ binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
+ _expr.setResult(left.storeRetainAccumulator());
- TempScope scope(_function);
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), *right);
- move(left, _block->TEMP(t), baseOp(ast->op));
- if (!_expr.accept(nx))
- _expr.code = left;
break;
}
+ case QSOperator::BitAnd:
+ case QSOperator::BitOr:
+ case QSOperator::BitXor:
+ if (left.isConst()) {
+ Reference right = expression(ast->right);
+ if (hasError)
+ return false;
+ _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ break;
+ }
+ // intentional fall-through!
case QSOperator::In:
case QSOperator::InstanceOf:
case QSOperator::Equal:
@@ -1337,55 +841,408 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::Le:
case QSOperator::Lt:
case QSOperator::StrictEqual:
- case QSOperator::StrictNotEqual: {
- TempScope scope(_function);
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), ast->operatorToken);
- left = _block->TEMP(t);
+ case QSOperator::StrictNotEqual:
+ case QSOperator::Add:
+ case QSOperator::Div:
+ case QSOperator::Mod:
+ case QSOperator::Mul:
+ case QSOperator::Sub:
+ case QSOperator::LShift:
+ case QSOperator::RShift:
+ case QSOperator::URShift: {
+ Reference right;
+ if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
+ visit(rhs);
+ right = _expr.result();
+ } else {
+ left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
+ right = expression(ast->right);
}
-
- Result right = expression(ast->right);
if (hasError)
return false;
- if (_expr.accept(cx)) {
- setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken);
+ _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+
+ break;
+ }
+
+ } // switch
+
+ return false;
+}
+
+Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right)
+{
+ switch (oper) {
+ case QSOperator::Add: {
+ //### Todo: when we add type hints, we can generate an Increment when both the lhs is a number and the rhs == 1
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Add add;
+ add.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(add);
+ break;
+ }
+ case QSOperator::Sub: {
+ if (right.isConst() && right.constant == Encode(int(1))) {
+ left.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Decrement());
} else {
- _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken);
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Sub sub;
+ sub.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(sub);
}
break;
}
-
- case QSOperator::Add:
+ case QSOperator::Mul: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Mul mul;
+ mul.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(mul);
+ break;
+ }
+ case QSOperator::Div: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Div div;
+ div.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(div);
+ break;
+ }
+ case QSOperator::Mod: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Mod mod;
+ mod.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(mod);
+ break;
+ }
case QSOperator::BitAnd:
+ if (right.isConst()) {
+ int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
+ if (left.isConst()) {
+ int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitAndConst bitAnd;
+ bitAnd.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitAnd);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitAnd bitAnd;
+ bitAnd.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitAnd);
+ }
+ break;
case QSOperator::BitOr:
+ if (right.isConst()) {
+ int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
+ if (left.isConst()) {
+ int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitOrConst bitOr;
+ bitOr.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitOr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitOr bitOr;
+ bitOr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitOr);
+ }
+ break;
case QSOperator::BitXor:
- case QSOperator::Div:
- case QSOperator::LShift:
- case QSOperator::Mod:
- case QSOperator::Mul:
+ if (right.isConst()) {
+ int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
+ if (left.isConst()) {
+ int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitXorConst bitXor;
+ bitXor.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitXor);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitXor bitXor;
+ bitXor.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitXor);
+ }
+ break;
+ case QSOperator::URShift:
+ if (right.isConst()) {
+ left.loadInAccumulator();
+ Instruction::UShrConst ushr;
+ ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(ushr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::UShr ushr;
+ ushr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(ushr);
+ }
+ break;
case QSOperator::RShift:
- case QSOperator::Sub:
- case QSOperator::URShift: {
- TempScope scope(_function);
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), ast->operatorToken);
- left = _block->TEMP(t);
+ if (right.isConst()) {
+ left.loadInAccumulator();
+ Instruction::ShrConst shr;
+ shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(shr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::Shr shr;
+ shr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(shr);
+ }
+ break;
+ case QSOperator::LShift:
+ if (right.isConst()) {
+ left.loadInAccumulator();
+ Instruction::ShlConst shl;
+ shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(shl);
+ } else {
+ right.loadInAccumulator();
+ Instruction::Shl shl;
+ shl.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(shl);
}
+ break;
+ case QSOperator::InstanceOf: {
+ Instruction::CmpInstanceOf binop;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ binop.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(binop);
+ break;
+ }
+ case QSOperator::In: {
+ Instruction::CmpIn binop;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ binop.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(binop);
+ break;
+ }
+ case QSOperator::StrictEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
- Result right = expression(ast->right);
- if (hasError)
- return false;
+ Instruction::CmpStrictEqual cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::StrictNotEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpStrictNotEqual cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Equal: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
- _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken);
+ Instruction::CmpEq cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
break;
}
+ case QSOperator::NotEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
- } // switch
+ Instruction::CmpNe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Gt: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
- return false;
+ Instruction::CmpGt cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Ge: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpGe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Lt: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpLt cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Le:
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpLe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ return Reference::fromAccumulator(this);
+}
+
+static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
+{
+ switch (oper) {
+ case QSOperator::StrictEqual: return QSOperator::StrictEqual;
+ case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual;
+ case QSOperator::Equal: return QSOperator::Equal;
+ case QSOperator::NotEqual: return QSOperator::NotEqual;
+ case QSOperator::Gt: return QSOperator::Le;
+ case QSOperator::Ge: return QSOperator::Lt;
+ case QSOperator::Lt: return QSOperator::Ge;
+ case QSOperator::Le: return QSOperator::Gt;
+ default: Q_UNIMPLEMENTED(); return QSOperator::Invalid;
+ }
+}
+
+Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
+{
+ if (left.isConst()) {
+ oper = operatorForSwappedOperands(oper);
+ qSwap(left, right);
+ }
+
+ if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
+ Value c = Primitive::fromReturnedValue(right.constant);
+ if (c.isNull() || c.isUndefined()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ } else if (c.isInt32()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+
+ }
+ }
+
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+
+ switch (oper) {
+ case QSOperator::StrictEqual: {
+ Instruction::CmpStrictEqual cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::StrictNotEqual: {
+ Instruction::CmpStrictNotEqual cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Equal: {
+ Instruction::CmpEq cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::NotEqual: {
+ Instruction::CmpNe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Gt: {
+ Instruction::CmpGt cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Ge: {
+ Instruction::CmpGe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Lt: {
+ Instruction::CmpLt cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Le: {
+ Instruction::CmpLe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+ return Reference();
}
bool Codegen::visit(CallExpression *ast)
@@ -1393,54 +1250,138 @@ bool Codegen::visit(CallExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
- IR::ExprList *args = 0, **args_it = &args;
- for (ArgumentList *it = ast->arguments; it; it = it->next) {
- Result arg = expression(it->expression);
- if (hasError)
- return false;
- IR::Expr *actual = argument(*arg);
- *args_it = _function->New<IR::ExprList>();
- (*args_it)->init(actual);
- args_it = &(*args_it)->next;
+ RegisterScope scope(this);
+
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
}
+
+ auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
- _expr.code = call(*base, args);
+
+ //### Do we really need all these call instructions? can's we load the callee in a temp?
+ if (base.type == Reference::Member) {
+ if (useFastLookups) {
+ Instruction::CallPropertyLookup call;
+ call.base = base.propertyBase.stackSlot();
+ call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ } else {
+ Instruction::CallProperty call;
+ call.base = base.propertyBase.stackSlot();
+ call.name = base.propertyNameIndex;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ }
+ } else if (base.type == Reference::Subscript) {
+ Instruction::CallElement call;
+ call.base = base.elementBase;
+ call.index = base.elementSubscript.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ } else if (base.type == Reference::Name) {
+ if (base.name == QStringLiteral("eval")) {
+ Instruction::CallPossiblyDirectEval call;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ } else if (useFastLookups && base.global) {
+ Instruction::CallGlobalLookup call;
+ call.index = registerGlobalGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ } else {
+ Instruction::CallName call;
+ call.name = base.nameAsIndex();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ }
+ } else {
+ base.loadInAccumulator();
+
+ Instruction::CallValue call;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
+Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
+{
+ int argc = 0;
+ for (ArgumentList *it = args; it; it = it->next)
+ ++argc;
+
+ if (!argc)
+ return { 0, 0 };
+
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
+
+ argc = 0;
+ for (ArgumentList *it = args; it; it = it->next) {
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ if (!argc && !it->next) {
+ // avoid copy for functions taking a single argument
+ if (e.isStackSlot())
+ return { 1, e.stackSlot() };
+ }
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata };
+}
+
bool Codegen::visit(ConditionalExpression *ast)
{
if (hasError)
return true;
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
- const unsigned t = _block->newTemp();
- TempScope scope(_function);
+ BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel();
+ condition(ast->expression, &iftrue, &iffalse, true);
- condition(ast->expression, iftrue, iffalse);
-
- _block = iftrue;
- Result ok = expression(ast->ok);
+ iftrue.link();
+ Reference ok = expression(ast->ok);
if (hasError)
return false;
- move(_block->TEMP(t), *ok);
- _block->JUMP(endif);
+ ok.loadInAccumulator();
+ BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
- _block = iffalse;
- Result ko = expression(ast->ko);
+ iffalse.link();
+ Reference ko = expression(ast->ko);
if (hasError)
return false;
- move(_block->TEMP(t), *ko);
- _block->JUMP(endif);
-
- _block = endif;
+ ko.loadInAccumulator();
- _expr.code = _block->TEMP(t);
+ jump_endif.link();
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
@@ -1450,48 +1391,59 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError)
return false;
- IR::Expr* expr = *expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- // Temporaries cannot be deleted
- IR::ArgLocal *al = expr->asArgLocal();
- if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) {
+
+ switch (expr.type) {
+ case Reference::StackSlot:
+ if (!expr.stackSlotIsLocalOrArgument)
+ break;
+ // fall through
+ case Reference::ScopedLocal:
// Trying to delete a function argument might throw.
- if (_function->isStrict) {
+ if (_context->isStrict) {
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
- _expr.code = _block->CONST(IR::BoolType, 0);
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
- }
- if (_function->isStrict && expr->asName()) {
- throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
+ case Reference::Name: {
+ if (_context->isStrict) {
+ throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
+ return false;
+ }
+ Instruction::DeleteName del;
+ del.name = expr.nameAsIndex();
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-
- // [[11.4.1]] Return true if it's not a reference
- if (expr->asConst() || expr->asString()) {
- _expr.code = _block->CONST(IR::BoolType, 1);
+ case Reference::Member: {
+ //### maybe add a variant where the base can be in the accumulator?
+ expr = expr.asLValue();
+ Instruction::DeleteMember del;
+ del.base = expr.propertyBase.stackSlot();
+ del.member = expr.propertyNameIndex;
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-
- // Return values from calls are also not a reference, but we have to
- // perform the call to allow for side effects.
- if (expr->asCall()) {
- _block->EXP(expr);
- _expr.code = _block->CONST(IR::BoolType, 1);
+ case Reference::Subscript: {
+ //### maybe add a variant where the index can be in the accumulator?
+ expr = expr.asLValue();
+ Instruction::DeleteSubscript del;
+ del.base = expr.elementBase;
+ del.index = expr.elementSubscript.stackSlot();
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
- if (expr->asTemp() ||
- (expr->asArgLocal() &&
- expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) {
- _expr.code = _block->CONST(IR::BoolType, 1);
- return false;
+ default:
+ break;
}
-
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(reference(expr));
- _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args);
+ // [[11.4.1]] Return true if it's not a reference
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -1500,11 +1452,7 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- _block->JUMP(_expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::BoolType, 0);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
@@ -1513,9 +1461,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
- if (!hasError)
- _expr.code = member(*base, _function->newString(ast->name.toString()));
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ _expr.setResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
@@ -1524,64 +1473,109 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
- _expr.code = _block->CLOSURE(function);
+ loadClosure(function);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-IR::Expr *Codegen::identifier(const QString &name, int line, int col)
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
{
- if (hasError)
- return 0;
-
- uint scope = 0;
- Environment *e = _variableEnvironment;
- IR::Function *f = _function;
+ int scope = 0;
+ Context *c = _context;
- while (f && e->parent) {
- if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name))
- return _block->NAME(name, line, col);
-
- int index = e->findMember(name);
- Q_ASSERT (index < e->members.size());
- if (index != -1) {
- IR::ArgLocal *al = _block->LOCAL(index, scope);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval"))
- al->isArgumentsOrEval = true;
- return al;
+ // skip the innermost context if it's simple (as the runtime won't
+ // create a context for it
+ if (c->canUseSimpleCall()) {
+ Context::Member m = c->findMember(name);
+ if (m.type != Context::UndefinedMember) {
+ Q_ASSERT((!m.canEscape));
+ Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/);
+ if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
+ r.isArgOrEval = true;
+ if (isLhs && c->isStrict)
+ // ### add correct source location
+ throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ }
+ return r;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict));
+ return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
+ }
+ c = c->parent;
+ }
+
+ while (c->parent) {
+ if (c->forceLookupByName())
+ goto loadByName;
+
+ Context::Member m = c->findMember(name);
+ if (m.type != Context::UndefinedMember) {
+ Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope)
+ : Reference::fromStackSlot(this, m.index, true /*isLocal*/);
+ if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
+ r.isArgOrEval = true;
+ if (isLhs && c->isStrict)
+ // ### add correct source location
+ throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ }
+ return r;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ int idx = argIdx + c->locals.size();
+ return Reference::fromScopedLocal(this, idx, scope);
+ } else {
+ Q_ASSERT(scope == 0);
+ return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
+ }
}
- const int argIdx = f->indexOfArgument(QStringRef(&name));
- if (argIdx != -1)
- return _block->ARG(argIdx, scope);
- if (!f->isStrict && f->hasDirectEval)
- return _block->NAME(name, line, col);
+ if (!c->isStrict && c->hasDirectEval)
+ goto loadByName;
++scope;
- e = e->parent;
- f = f->outer;
+ c = c->parent;
}
- // This hook allows implementing QML lookup semantics
- if (IR::Expr *fallback = fallbackNameLookup(name, line, col))
- return fallback;
+ {
+ // This hook allows implementing QML lookup semantics
+ Reference fallback = fallbackNameLookup(name);
+ if (fallback.type != Reference::Invalid)
+ return fallback;
+ }
- if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding)
- return _block->GLOBALNAME(name, line, col);
+ if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) {
+ Reference r = Reference::fromName(this, name);
+ r.global = true;
+ return r;
+ }
// global context or with. Lookup by name
- return _block->NAME(name, line, col);
+ loadByName:
+ return Reference::fromName(this, name);
+}
+void Codegen::loadClosure(int closureId)
+{
+ if (closureId >= 0) {
+ Instruction::LoadClosure load;
+ load.value = closureId;
+ bytecodeGenerator->addInstruction(load);
+ } else {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ }
}
-IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col)
+Codegen::Reference Codegen::fallbackNameLookup(const QString &name)
{
Q_UNUSED(name)
- Q_UNUSED(line)
- Q_UNUSED(col)
- return 0;
+ return Reference();
}
bool Codegen::visit(IdentifierExpression *ast)
@@ -1589,7 +1583,7 @@ bool Codegen::visit(IdentifierExpression *ast)
if (hasError)
return false;
- _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn);
+ _expr.setResult(referenceForName(ast->name.toString(), false));
return false;
}
@@ -1606,18 +1600,21 @@ bool Codegen::visit(NewExpression *ast)
{
if (hasError)
return false;
- TempScope scope(_function);
- Result base = expression(ast->expression);
+ RegisterScope scope(this);
+
+ Reference base = expression(ast->expression);
if (hasError)
return false;
- IR::Expr *expr = *base;
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- _expr.code = _block->NEW(expr, 0);
+ //### Maybe create a ConstructA that takes an accumulator?
+ base = base.storeOnStack();
+
+ Instruction::Construct create;
+ create.func = base.stackSlot();
+ create.argc = 0;
+ create.argv = 0;
+ bytecodeGenerator->addInstruction(create);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
@@ -1626,32 +1623,23 @@ bool Codegen::visit(NewMemberExpression *ast)
if (hasError)
return false;
- const unsigned t = _block->newTemp();
+ RegisterScope scope(this);
- TempScope scope(_function);
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ base = base.storeOnStack();
- Result base = expression(ast->base);
+ auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
- IR::Expr *expr = *base;
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- IR::ExprList *args = 0, **args_it = &args;
- for (ArgumentList *it = ast->arguments; it; it = it->next) {
- Result arg = expression(it->expression);
- if (hasError)
- return false;
- IR::Expr *actual = argument(*arg);
- *args_it = _function->New<IR::ExprList>();
- (*args_it)->init(actual);
- args_it = &(*args_it)->next;
- }
- move(_block->TEMP(t), _block->NEW(expr, args));
- _expr.code = _block->TEMP(t);
+ Instruction::Construct create;
+ create.func = base.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
@@ -1660,14 +1648,7 @@ bool Codegen::visit(NotExpression *ast)
if (hasError)
return false;
- const unsigned r = _block->newTemp();
- TempScope scope(_function);
-
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken);
- _expr.code = _block->TEMP(r);
+ _expr.setResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -1676,8 +1657,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
- if (_expr.accept(cx)) _block->JUMP(_expr.iffalse);
- else _expr.code = _block->CONST(IR::NullType, 0);
+ if (_expr.accept(cx))
+ bytecodeGenerator->jump().link(*_expr.iffalse());
+ else
+ _expr.setResult(Reference::fromConst(this, Encode::null()));
return false;
}
@@ -1687,30 +1670,10 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- if (ast->value) _block->JUMP(_expr.iftrue);
- else _block->JUMP(_expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::NumberType, ast->value);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
-struct ObjectPropertyValue {
- ObjectPropertyValue()
- : value(0)
- , getter(-1)
- , setter(-1)
- {}
-
- IR::Expr *value;
- int getter; // index in _module->functions or -1 if not set
- int setter;
-
- bool hasGetter() const { return getter >= 0; }
- bool hasSetter() const { return setter >= 0; }
-};
-
bool Codegen::visit(ObjectLiteral *ast)
{
if (hasError)
@@ -1718,33 +1681,27 @@ bool Codegen::visit(ObjectLiteral *ast)
QMap<QString, ObjectPropertyValue> valueMap;
- const unsigned t = _block->newTemp();
- TempScope scope(_function);
+ RegisterScope scope(this);
for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
QString name = it->assignment->name->asString();
if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
- Result value = expression(nv->value);
+ Reference value = expression(nv->value);
if (hasError)
return false;
+
ObjectPropertyValue &v = valueMap[name];
- if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) {
+ if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) {
throwSyntaxError(nv->lastSourceLocation(),
QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
return false;
}
- if (IR::Const *c = (*value)->asConst()) {
- valueMap[name].value = c;
- } else {
- unsigned t = _block->newTemp();
- move(_block->TEMP(t), *value);
- valueMap[name].value = _block->TEMP(t);
- }
+ v.rvalue = value.storeOnStack();
} else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0);
ObjectPropertyValue &v = valueMap[name];
- if (v.value ||
+ if (v.rvalue.isValid() ||
(gs->type == PropertyGetterSetter::Getter && v.hasGetter()) ||
(gs->type == PropertyGetterSetter::Setter && v.hasSetter())) {
throwSyntaxError(gs->lastSourceLocation(),
@@ -1760,96 +1717,90 @@ bool Codegen::visit(ObjectLiteral *ast)
}
}
- // The linked-list arguments to builtin_define_object_literal
- // begin with a CONST counting the number of key/value pairs, followed by the
- // key value pairs, followed by the array entries.
- IR::ExprList *args = _function->New<IR::ExprList>();
-
- IR::Const *entryCountParam = _function->New<IR::Const>();
- entryCountParam->init(IR::SInt32Type, 0);
- args->expr = entryCountParam;
- args->next = 0;
-
- IR::ExprList *keyValueEntries = 0;
- IR::ExprList *currentKeyValueEntry = 0;
- int keyValueEntryCount = 0;
- IR::ExprList *arrayEntries = 0;
+ QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter;
+ bool needSparseArray = false; // set to true if any array index is bigger than 16
- IR::ExprList *currentArrayEntry = 0;
-
- for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) {
- IR::ExprList **currentPtr = 0;
- uint keyAsIndex = QV4::String::toArrayIndex(it.key());
- if (keyAsIndex != UINT_MAX) {
- if (!arrayEntries) {
- arrayEntries = _function->New<IR::ExprList>();
- currentArrayEntry = arrayEntries;
- } else {
- currentArrayEntry->next = _function->New<IR::ExprList>();
- currentArrayEntry = currentArrayEntry->next;
- }
- currentPtr = &currentArrayEntry;
- IR::Const *idx = _function->New<IR::Const>();
- idx->init(IR::UInt32Type, keyAsIndex);
- (*currentPtr)->expr = idx;
+ for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end();
+ it != eit; ++it) {
+ QString name = it.key();
+ uint keyAsIndex = QV4::String::toArrayIndex(name);
+ if (keyAsIndex != std::numeric_limits<uint>::max()) {
+ it->keyAsIndex = keyAsIndex;
+ if (keyAsIndex > 16)
+ needSparseArray = true;
+ if (it->hasSetter() || it->hasGetter())
+ arrayKeyWithGetterSetter.append(name);
+ else
+ arrayKeyWithValue.append(name);
} else {
- if (!keyValueEntries) {
- keyValueEntries = _function->New<IR::ExprList>();
- currentKeyValueEntry = keyValueEntries;
- } else {
- currentKeyValueEntry->next = _function->New<IR::ExprList>();
- currentKeyValueEntry = currentKeyValueEntry->next;
- }
- currentPtr = &currentKeyValueEntry;
- (*currentPtr)->expr = _block->NAME(it.key(), 0, 0);
- keyValueEntryCount++;
+ nonArrayKey.append(name);
}
+ }
- IR::ExprList *&current = *currentPtr;
- if (it->value) {
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->CONST(IR::BoolType, true);
-
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = it->value;
+ int args = -1;
+ auto push = [this, &args](const Reference &arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ (void) arg.storeOnStack(temp);
+ };
+
+ QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members;
+
+ Reference acc = Reference::fromAccumulator(this);
+ // generate the key/value pairs
+ for (const QString &key : qAsConst(nonArrayKey)) {
+ const ObjectPropertyValue &prop = valueMap[key];
+
+ if (prop.hasGetter() || prop.hasSetter()) {
+ Q_ASSERT(!prop.rvalue.isValid());
+ loadClosure(prop.getter);
+ push(acc);
+ loadClosure(prop.setter);
+ push(acc);
+ members.append({ key, true });
} else {
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->CONST(IR::BoolType, false);
-
- unsigned getter = _block->newTemp();
- unsigned setter = _block->newTemp();
- move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0));
- move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0));
-
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->TEMP(getter);
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->TEMP(setter);
+ Q_ASSERT(prop.rvalue.isValid());
+ push(prop.rvalue);
+ members.append({ key, false });
}
-
- it = valueMap.erase(it);
}
- entryCountParam->value = keyValueEntryCount;
+ // generate array entries with values
+ for (const QString &key : qAsConst(arrayKeyWithValue)) {
+ const ObjectPropertyValue &prop = valueMap[key];
+ Q_ASSERT(!prop.hasGetter() && !prop.hasSetter());
+ push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
+ push(prop.rvalue);
+ }
- if (keyValueEntries)
- args->next = keyValueEntries;
- if (arrayEntries) {
- if (currentKeyValueEntry)
- currentKeyValueEntry->next = arrayEntries;
- else
- args->next = arrayEntries;
+ // generate array entries with both a value and a setter
+ for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) {
+ const ObjectPropertyValue &prop = valueMap[key];
+ Q_ASSERT(!prop.rvalue.isValid());
+ push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
+ loadClosure(prop.getter);
+ push(acc);
+ loadClosure(prop.setter);
+ push(acc);
}
- move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args));
+ int classId = jsUnitGenerator->registerJSClass(members);
+
+ uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size();
+ arrayGetterSetterCountAndFlags |= needSparseArray << 30;
- _expr.code = _block->TEMP(t);
+ if (args == -1)
+ args = 0;
+
+ Instruction::DefineObjectLiteral call;
+ call.internalClassId = classId;
+ call.arrayValueCount = arrayKeyWithValue.size();
+ call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
@@ -1858,26 +1809,17 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->base);
+ Reference expr = expression(ast->base);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- const unsigned oldValue = _block->newTemp();
- setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken);
-
- TempScope scope(_function);
- const unsigned newValue = _block->newTemp();
- setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken);
- setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken);
-
- if (!_expr.accept(nx))
- _expr.code = _block->TEMP(oldValue);
+ _expr.setResult(unop(PostDecrement, expr));
return false;
}
@@ -1887,54 +1829,35 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->base);
+ Reference expr = expression(ast->base);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- const unsigned oldValue = _block->newTemp();
- setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken);
-
- TempScope scope(_function);
- const unsigned newValue = _block->newTemp();
- setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken);
- setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken);
-
- if (!_expr.accept(nx))
- _expr.code = _block->TEMP(oldValue);
-
+ _expr.setResult(unop(PostIncrement, expr));
return false;
}
bool Codegen::visit(PreDecrementExpression *ast)
-{
- if (hasError)
+{ if (hasError)
return false;
- Result expr = expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- IR::Expr *op = binop(IR::OpSub, *expr, _block->CONST(IR::NumberType, 1), ast->decrementToken);
- if (_expr.accept(nx)) {
- setLocation(move(*expr, op), ast->decrementToken);
- } else {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), op), ast->decrementToken);
- setLocation(move(*expr, _block->TEMP(t)), ast->decrementToken);
- _expr.code = _block->TEMP(t);
- }
+ _expr.setResult(unop(PreDecrement, expr));
return false;
}
@@ -1943,25 +1866,17 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- IR::Expr *op = binop(IR::OpAdd, unop(IR::OpUPlus, *expr, ast->incrementToken), _block->CONST(IR::NumberType, 1), ast->incrementToken);
- if (_expr.accept(nx)) {
- setLocation(move(*expr, op), ast->incrementToken);
- } else {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), op), ast->incrementToken);
- setLocation(move(*expr, _block->TEMP(t)), ast->incrementToken);
- _expr.code = _block->TEMP(t);
- }
+ _expr.setResult(unop(PreIncrement, expr));
return false;
}
@@ -1970,7 +1885,13 @@ bool Codegen::visit(RegExpLiteral *ast)
if (hasError)
return false;
- _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags);
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+ _expr.setResult(r);
+
+ Instruction::LoadRegExp instr;
+ instr.regExpId = jsUnitGenerator->registerRegExp(ast);
+ bytecodeGenerator->addInstruction(instr);
return false;
}
@@ -1979,16 +1900,22 @@ bool Codegen::visit(StringLiteral *ast)
if (hasError)
return false;
- _expr.code = _block->STRING(_function->newString(ast->value.toString()));
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+ _expr.setResult(r);
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
return false;
}
-bool Codegen::visit(ThisExpression *ast)
+bool Codegen::visit(ThisExpression *)
{
if (hasError)
return false;
- _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn);
+ _expr.setResult(Reference::fromThis(this));
return false;
}
@@ -1997,14 +1924,7 @@ bool Codegen::visit(TildeExpression *ast)
if (hasError)
return false;
- const unsigned t = _block->newTemp();
- TempScope scope(_function);
-
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken);
- _expr.code = _block->TEMP(t);
+ _expr.setResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -2013,11 +1933,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- _block->JUMP(_expr.iftrue);
- } else {
- _expr.code = _block->CONST(IR::BoolType, 1);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2026,14 +1942,24 @@ bool Codegen::visit(TypeOfExpression *ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
- Result expr = expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(reference(*expr));
- _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+
+ if (expr.type == Reference::Name) {
+ // special handling as typeof doesn't throw here
+ Instruction::TypeofName instr;
+ instr.name = expr.nameAsIndex();
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ Instruction::TypeofValue instr;
+ bytecodeGenerator->addInstruction(instr);
+ }
+ _expr.setResult(Reference::fromAccumulator(this));
+
return false;
}
@@ -2042,12 +1968,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken);
- _expr.code = _block->TEMP(t);
+ _expr.setResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2056,12 +1977,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken);
- _expr.code = _block->TEMP(t);
+ _expr.setResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2070,10 +1986,10 @@ bool Codegen::visit(VoidExpression *ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
statement(ast->expression);
- _expr.code = _block->CONST(IR::UndefinedType, 0);
+ _expr.setResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@@ -2082,146 +1998,190 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
- if (_variableEnvironment->compilationMode == QmlBinding)
- move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0));
+ if (_context->compilationMode == QmlBinding)
+ Reference::fromName(this, ast->name.toString()).loadInAccumulator();
_expr.accept(nx);
return false;
}
+static bool endsWithReturn(Node *node)
+{
+ if (!node)
+ return false;
+ if (AST::cast<ReturnStatement *>(node))
+ return true;
+ if (AST::cast<ThrowStatement *>(node))
+ return true;
+ if (Program *p = AST::cast<Program *>(node))
+ return endsWithReturn(p->elements);
+ if (SourceElements *se = AST::cast<SourceElements *>(node)) {
+ while (se->next)
+ se = se->next;
+ return endsWithReturn(se->element);
+ }
+ if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node))
+ return endsWithReturn(sse->statement);
+ if (StatementList *sl = AST::cast<StatementList *>(node)) {
+ while (sl->next)
+ sl = sl->next;
+ return endsWithReturn(sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node))
+ return endsWithReturn(b->statements);
+ if (IfStatement *is = AST::cast<IfStatement *>(node))
+ return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko);
+ return false;
+}
+
int Codegen::defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body,
- const QStringList &inheritedLocals)
-{
- Loop *loop = 0;
- qSwap(_loop, loop);
- QStack<IR::BasicBlock *> exceptionHandlers;
- qSwap(_exceptionHandlers, exceptionHandlers);
-
- ScopeAndFinally *scopeAndFinally = 0;
-
- enterEnvironment(ast);
- IR::Function *function = _module->newFunction(name, _function);
- int functionIndex = _module->functions.count() - 1;
-
- IR::BasicBlock *entryBlock = function->newBasicBlock(0);
- IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock);
- function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode
- || _module->debugMode; // Conditional breakpoints are like eval in the function
- function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed);
- function->usesThis = _variableEnvironment->usesThis;
- function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount);
- function->isStrict = _variableEnvironment->isStrict;
- function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression;
- function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding;
-
- AST::SourceLocation loc = ast->firstSourceLocation();
- function->line = loc.startLine;
- function->column = loc.startColumn;
-
- if (function->usesArgumentsObject)
- _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
+ AST::SourceElements *body)
+{
+ Q_UNUSED(formals);
+
+ enterContext(ast);
+
+ if (_context->functionIndex >= 0)
+ // already defined
+ return leaveContext();
+
+ _context->name = name;
+ _module->functions.append(_context);
+ _context->functionIndex = _module->functions.count() - 1;
+
+ _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function
+
+ BytecodeGenerator bytecode(_context->line, _module->debugMode);
+ BytecodeGenerator *savedBytecodeGenerator;
+ savedBytecodeGenerator = bytecodeGenerator;
+ bytecodeGenerator = &bytecode;
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
+
+ // reserve the js stack frame (Context & js Function & accumulator)
+ bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+
+ int returnAddress = -1;
+ bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode);
+ qSwap(requiresReturnValue, _requiresReturnValue);
+ if (requiresReturnValue)
+ returnAddress = bytecodeGenerator->newRegister();
+ if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed)
+ _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
+
+ bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval;
+ if (_context->compilationMode == QmlBinding // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not.
+ || (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode &&
+ (_context->compilationMode != EvalCode || _context->isStrict))) {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
// variables in global code are properties of the global context object, not locals as with other functions.
- if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) {
- unsigned t = 0;
- for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) {
+ if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) {
+ for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) {
const QString &local = it.key();
- function->LOCAL(local);
- (*it).index = t;
- entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(IR::UndefinedType, 0));
- ++t;
- }
- } else {
- if (!_variableEnvironment->isStrict) {
- for (const QString &inheritedLocal : qAsConst(inheritedLocals)) {
- function->LOCAL(inheritedLocal);
- unsigned tempIndex = entryBlock->newTemp();
- Environment::Member member = { Environment::UndefinedMember,
- static_cast<int>(tempIndex), 0,
- AST::VariableDeclaration::VariableScope::FunctionScope };
- _variableEnvironment->members.insert(inheritedLocal, member);
+ if (allVarsEscape)
+ it->canEscape = true;
+ if (it->canEscape) {
+ it->index = _context->locals.size();
+ _context->locals.append(local);
+ if (it->type == Context::ThisFunctionName) {
+ // move the name from the stack to the call context
+ Instruction::LoadReg load;
+ load.reg = CallData::Function;
+ bytecodeGenerator->addInstruction(load);
+ Instruction::StoreLocal store;
+ store.index = it->index;
+ bytecodeGenerator->addInstruction(store);
+ }
+ } else {
+ if (it->type == Context::ThisFunctionName)
+ it->index = CallData::Function;
+ else
+ it->index = bytecodeGenerator->newRegister();
}
}
-
- IR::ExprList *args = 0;
- for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) {
+ } else {
+ for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) {
const QString &local = it.key();
- IR::ExprList *next = function->New<IR::ExprList>();
- next->expr = entryBlock->NAME(local, 0, 0);
- next->next = args;
- args = next;
- }
- if (args) {
- IR::ExprList *next = function->New<IR::ExprList>();
- next->expr = entryBlock->CONST(IR::BoolType, false); // ### Investigate removal of bool deletable
- next->next = args;
- args = next;
- entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args));
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = false;
+ declareVar.varName = registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
}
}
- unsigned returnAddress = entryBlock->newTemp();
-
- entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0));
- setLocation(exitBlock->RET(exitBlock->TEMP(returnAddress)), ast->lastSourceLocation());
-
- qSwap(_function, function);
- qSwap(_block, entryBlock);
- qSwap(_exitBlock, exitBlock);
qSwap(_returnAddress, returnAddress);
- qSwap(_scopeAndFinally, scopeAndFinally);
-
- for (FormalParameterList *it = formals; it; it = it->next) {
- _function->RECEIVE(it->name.toString());
- }
-
- for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) {
+ for (const Context::Member &member : qAsConst(_context->members)) {
if (member.function) {
const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
member.function->body ? member.function->body->elements : 0);
- if (! _variableEnvironment->parent) {
- move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
- _block->CLOSURE(function));
+ loadClosure(function);
+ if (! _context->parent) {
+ Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator();
} else {
Q_ASSERT(member.index >= 0);
- move(_block->LOCAL(member.index, 0), _block->CLOSURE(function));
+ Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0)
+ : Reference::fromStackSlot(this, member.index, true);
+ local.storeConsumeAccumulator();
}
}
}
- if (_function->usesArgumentsObject) {
- move(identifier(QStringLiteral("arguments"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn),
- _block->CALL(_block->NAME(IR::Name::builtin_setup_argument_object,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0));
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ if (_context->isStrict) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
}
- if (_function->usesThis && !_function->isStrict) {
+ if (_context->usesThis && !_context->isStrict) {
// make sure we convert this to an object
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_convert_this_to_object,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0));
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
}
beginFunctionBodyHook();
sourceElements(body);
- _function->addBasicBlock(_exitBlock);
+ if (hasError || !endsWithReturn(body)) {
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
+ if (requiresReturnValue) {
+ if (_returnAddress >= 0) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
+ }
+ } else {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ }
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
- _block->JUMP(_exitBlock);
+ bytecodeGenerator->finalize(_context);
+ _context->registerCount = bytecodeGenerator->registerCount();
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
+ << "register count" << _context->registerCount;
+ QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
+ _context->line, _context->lineNumberMapping);
+ qDebug();
+ }
- qSwap(_function, function);
- qSwap(_block, entryBlock);
- qSwap(_exitBlock, exitBlock);
qSwap(_returnAddress, returnAddress);
- qSwap(_scopeAndFinally, scopeAndFinally);
- qSwap(_exceptionHandlers, exceptionHandlers);
- qSwap(_loop, loop);
-
- leaveEnvironment();
+ qSwap(requiresReturnValue, _requiresReturnValue);
+ bytecodeGenerator = savedBytecodeGenerator;
- return functionIndex;
+ return leaveContext();
}
bool Codegen::visit(FunctionSourceElement *ast)
@@ -2247,11 +2207,9 @@ bool Codegen::visit(Block *ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
- for (StatementList *it = ast->statements; it; it = it->next) {
- statement(it->statement);
- }
+ statementList(ast->statements);
return false;
}
@@ -2260,27 +2218,22 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- TempScope scope(_function);
-
- if (!_loop) {
+ if (!_context->controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- Loop *loop = 0;
- if (ast->label.isEmpty())
- loop = _loop;
- else {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->labelledStatement && loop->labelledStatement->label == ast->label)
- break;
- }
- if (!loop) {
+
+ ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
+ if (h.type == ControlFlow::Invalid) {
+ if (ast->label.isEmpty())
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
+ else
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
- return false;
- }
+ return false;
}
- unwindException(loop->scopeAndFinally);
- _block->JUMP(loop->breakBlock);
+
+ _context->controlFlow->jumpToHandler(h);
+
return false;
}
@@ -2289,33 +2242,24 @@ bool Codegen::visit(ContinueStatement *ast)
if (hasError)
return false;
- TempScope scope(_function);
+ RegisterScope scope(this);
- Loop *loop = 0;
- if (ast->label.isEmpty()) {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->continueBlock)
- break;
- }
- } else {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->labelledStatement && loop->labelledStatement->label == ast->label) {
- if (!loop->continueBlock)
- loop = 0;
- break;
- }
- }
- if (!loop) {
- throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
- return false;
- }
+ if (!_context->controlFlow) {
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
+ return false;
}
- if (!loop) {
- throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop"));
+
+ ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
+ if (h.type == ControlFlow::Invalid) {
+ if (ast->label.isEmpty())
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
+ else
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop"));
return false;
}
- unwindException(loop->scopeAndFinally);
- _block->JUMP(loop->continueBlock);
+
+ _context->controlFlow->jumpToHandler(h);
+
return false;
}
@@ -2330,26 +2274,27 @@ bool Codegen::visit(DoWhileStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
+ RegisterScope scope(this);
- IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler());
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+ BytecodeGenerator::Label cond = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- enterLoop(ast, loopend, loopcond);
+ ControlFlowLoop flow(this, &end, &cond);
- _block->JUMP(loopbody);
-
- _block = loopbody;
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken);
- _block = loopcond;
- condition(ast->expression, loopbody, loopend);
+ cond.link();
- _block = loopend;
+ if (!AST::cast<FalseLiteral *>(ast->expression)) {
+ if (AST::cast<TrueLiteral *>(ast->expression))
+ bytecodeGenerator->jump().link(body);
+ else
+ condition(ast->expression, &body, &end, false);
+ }
- leaveLoop();
+ end.link();
return false;
}
@@ -2367,12 +2312,13 @@ bool Codegen::visit(ExpressionStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
+ RegisterScope scope(this);
- if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) {
- Result e = expression(ast->expression);
- if (*e)
- move(_block->TEMP(_returnAddress), *e);
+ if (requiresReturnValue) {
+ Reference e = expression(ast->expression);
+ if (hasError)
+ return false;
+ (void) e.storeOnStack(_returnAddress);
} else {
statement(ast->expression);
}
@@ -2384,46 +2330,44 @@ bool Codegen::visit(ForEachStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
-
- IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
- int objectToIterateOn = _block->newTemp();
- Result expr = expression(ast->expression);
+ Reference obj = Reference::fromStackSlot(this);
+ Reference expr = expression(ast->expression);
if (hasError)
- return false;
- move(_block->TEMP(objectToIterateOn), *expr);
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(objectToIterateOn));
+ return true;
- int iterator = _block->newTemp();
- move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+ expr.loadInAccumulator();
+ Instruction::ForeachIteratorObject iteratorObjInstr;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ obj.storeConsumeAccumulator();
- enterLoop(ast, foreachend, foreachin);
- _block->JUMP(foreachin);
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+
+ bytecodeGenerator->jump().link(in);
+
+ ControlFlowLoop flow(this, &end, &in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
- _block = foreachbody;
- int temp = _block->newTemp();
- Result init = expression(ast->initialiser);
- if (hasError)
- return false;
- move(*init, _block->TEMP(temp));
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+
+ in.link();
+
+ Reference lhs = expression(ast->initialiser).asLValue();
- _block = foreachin;
+ obj.loadInAccumulator();
+ Instruction::ForeachNextPropertyName nextPropInstr;
+ bytecodeGenerator->addInstruction(nextPropInstr);
+ lhs = lhs.storeRetainAccumulator().storeOnStack();
- args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
- int null = _block->newTemp();
- move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
- setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken);
- _block = foreachend;
+ Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
+ bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body);
+
+ end.link();
- leaveLoop();
return false;
}
@@ -2432,35 +2376,28 @@ bool Codegen::visit(ForStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
-
- IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
statement(ast->initialiser);
- _block->JUMP(forcond);
- enterLoop(ast, forend, forstep);
+ BytecodeGenerator::Label cond = bytecodeGenerator->label();
+ BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- _block = forcond;
- if (ast->condition)
- condition(ast->condition, forbody, forend);
- else
- _block->JUMP(forbody);
+ ControlFlowLoop flow(this, &end, &step);
+
+ condition(ast->condition, &body, &end, true);
- _block = forbody;
+ body.link();
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- _block = forstep;
+ step.link();
statement(ast->expression);
- _block->JUMP(forcond);
+ bytecodeGenerator->jump().link(cond);
- _block = forend;
-
- leaveLoop();
+ end.link();
return false;
}
@@ -2470,26 +2407,28 @@ bool Codegen::visit(IfStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
-
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0;
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
- condition(ast->expression, iftrue, ast->ko ? iffalse : endif);
+ BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel();
+ condition(ast->expression, &trueLabel, &falseLabel, true);
- _block = iftrue;
+ trueLabel.link();
statement(ast->ok);
- setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken);
-
if (ast->ko) {
- _block = iffalse;
- statement(ast->ko);
- setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken);
+ if (endsWithReturn(ast)) {
+ falseLabel.link();
+ statement(ast->ko);
+ } else {
+ BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
+ falseLabel.link();
+ statement(ast->ko);
+ jump_endif.link();
+ }
+ } else {
+ falseLabel.link();
}
- _block = endif;
-
return false;
}
@@ -2498,12 +2437,12 @@ bool Codegen::visit(LabelledStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
+ RegisterScope scope(this);
// check that no outer loop contains the label
- Loop *l = _loop;
+ ControlFlow *l = _context->controlFlow;
while (l) {
- if (l->labelledStatement && l->labelledStatement->label == ast->label) {
+ if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
throwSyntaxError(ast->firstSourceLocation(), error);
return false;
@@ -2521,12 +2460,10 @@ bool Codegen::visit(LabelledStatement *ast)
AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
} else {
- IR::BasicBlock *breakBlock = _function->newBasicBlock(exceptionHandler());
- enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0);
+ BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
+ ControlFlowLoop flow(this, &breakLabel);
statement(ast->statement);
- _block->JUMP(breakBlock);
- _block = breakBlock;
- leaveLoop();
+ breakLabel.link();
}
return false;
@@ -2537,40 +2474,44 @@ bool Codegen::visit(LocalForEachStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
+ RegisterScope scope(this);
- IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
+ Reference obj = Reference::fromStackSlot(this);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return true;
variableDeclaration(ast->declaration);
- int iterator = _block->newTemp();
- move(_block->TEMP(iterator), *expression(ast->expression));
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+ expr.loadInAccumulator();
+ Instruction::ForeachIteratorObject iteratorObjInstr;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ obj.storeConsumeAccumulator();
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- _block->JUMP(foreachin);
- enterLoop(ast, foreachend, foreachin);
+ bytecodeGenerator->jump().link(in);
+ ControlFlowLoop flow(this, &end, &in);
- _block = foreachbody;
- int temp = _block->newTemp();
- move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue();
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+
+ in.link();
- _block = foreachin;
+ obj.loadInAccumulator();
+ Instruction::ForeachNextPropertyName nextPropInstr;
+ bytecodeGenerator->addInstruction(nextPropInstr);
+ auto lhs = it.storeRetainAccumulator().storeOnStack();
- args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
- int null = _block->newTemp();
- move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
- setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken);
- _block = foreachend;
+ Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
+ bytecodeGenerator->jumpStrictNotEqual(lhs.stackSlot(), body);
+
+ end.link();
- leaveLoop();
return false;
}
@@ -2579,35 +2520,27 @@ bool Codegen::visit(LocalForStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
-
- IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
variableDeclarationList(ast->declarations);
- _block->JUMP(forcond);
- enterLoop(ast, forend, forstep);
+ BytecodeGenerator::Label cond = bytecodeGenerator->label();
+ BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- _block = forcond;
- if (ast->condition)
- condition(ast->condition, forbody, forend);
- else
- _block->JUMP(forbody);
+ ControlFlowLoop flow(this, &end, &step);
+
+ condition(ast->condition, &body, &end, true);
- _block = forbody;
+ body.link();
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- _block = forstep;
+ step.link();
statement(ast->expression);
- _block->JUMP(forcond);
-
- _block = forend;
-
- leaveLoop();
+ bytecodeGenerator->jump().link(cond);
+ end.link();
return false;
}
@@ -2617,24 +2550,30 @@ bool Codegen::visit(ReturnStatement *ast)
if (hasError)
return true;
- if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) {
+ if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
+ Reference expr;
if (ast->expression) {
- Result expr = expression(ast->expression);
- move(_block->TEMP(_returnAddress), *expr);
+ expr = expression(ast->expression);
+ if (hasError)
+ return false;
+ } else {
+ expr = Reference::fromConst(this, Encode::undefined());
}
- // Since we're leaving, don't let any finally statements we emit as part of the unwinding
- // jump to exception handlers at run-time if they throw.
- IR::BasicBlock *unwindBlock = _function->newBasicBlock(/*no exception handler*/nullptr);
- _block->JUMP(unwindBlock);
- _block = unwindBlock;
-
- unwindException(0);
-
- _block->JUMP(_exitBlock);
+ if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) {
+ if (_returnAddress >= 0)
+ (void) expr.storeOnStack(_returnAddress);
+ else
+ expr.loadInAccumulator();
+ ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return);
+ _context->controlFlow->jumpToHandler(h);
+ } else {
+ expr.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
return false;
}
@@ -2643,245 +2582,151 @@ bool Codegen::visit(SwitchStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
-
- IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
if (ast->block) {
- int lhs = _block->newTemp();
- move(_block->TEMP(lhs), *expression(ast->expression));
- IR::BasicBlock *switchcond = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(switchcond);
- IR::BasicBlock *previousBlock = 0;
-
- QHash<Node *, IR::BasicBlock *> blockMap;
-
- enterLoop(ast, switchend, 0);
+ BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
+ Reference lhs = expression(ast->expression);
+ if (hasError)
+ return false;
+ lhs = lhs.storeOnStack();
+
+ // set up labels for all clauses
+ QHash<Node *, BytecodeGenerator::Label> blockMap;
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next)
+ blockMap[it->clause] = bytecodeGenerator->newLabel();
+ if (ast->block->defaultClause)
+ blockMap[ast->block->defaultClause] = bytecodeGenerator->newLabel();
+ for (CaseClauses *it = ast->block->moreClauses; it; it = it->next)
+ blockMap[it->clause] = bytecodeGenerator->newLabel();
+
+ // do the switch conditions
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
-
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[clause] = _block;
-
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
-
- for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
- statement(it2->statement);
-
- previousBlock = _block;
- }
-
- if (ast->block->defaultClause) {
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[ast->block->defaultClause] = _block;
-
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
-
- for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next)
- statement(it2->statement);
-
- previousBlock = _block;
+ Reference rhs = expression(clause->expression);
+ if (hasError)
+ return false;
+ rhs.loadInAccumulator();
+ bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
}
for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
CaseClause *clause = it->clause;
+ Reference rhs = expression(clause->expression);
+ if (hasError)
+ return false;
+ rhs.loadInAccumulator();
+ bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
+ }
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[clause] = _block;
+ if (DefaultClause *defaultClause = ast->block->defaultClause)
+ bytecodeGenerator->jump().link(blockMap.value(defaultClause));
+ else
+ bytecodeGenerator->jump().link(switchEnd);
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
+ ControlFlowLoop flow(this, &switchEnd);
- for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
- statement(it2->statement);
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+ blockMap[clause].link();
- previousBlock = _block;
+ statementList(clause->statements);
}
- leaveLoop();
-
- _block->JUMP(switchend);
+ if (ast->block->defaultClause) {
+ DefaultClause *clause = ast->block->defaultClause;
+ blockMap[clause].link();
- _block = switchcond;
- for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
- CaseClause *clause = it->clause;
- Result rhs = expression(clause->expression);
- IR::BasicBlock *iftrue = blockMap[clause];
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken);
- _block = iffalse;
+ statementList(clause->statements);
}
for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
CaseClause *clause = it->clause;
- Result rhs = expression(clause->expression);
- IR::BasicBlock *iftrue = blockMap[clause];
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken);
- _block = iffalse;
- }
+ blockMap[clause].link();
- if (DefaultClause *defaultClause = ast->block->defaultClause) {
- setLocation(_block->JUMP(blockMap[ast->block->defaultClause]), defaultClause->defaultToken);
+ statementList(clause->statements);
}
- }
- _block->JUMP(switchend);
+ switchEnd.link();
+
+ }
- _block = switchend;
return false;
}
bool Codegen::visit(ThrowStatement *ast)
{
if (hasError)
- return true;
+ return false;
+
+ RegisterScope scope(this);
- TempScope scope(_function);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return false;
- Result expr = expression(ast->expression);
- move(_block->TEMP(_returnAddress), *expr);
- IR::ExprList *throwArgs = _function->New<IR::ExprList>();
- throwArgs->expr = _block->TEMP(_returnAddress);
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
+ if (_context->controlFlow) {
+ _context->controlFlow->handleThrow(expr);
+ } else {
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
+ }
return false;
}
-bool Codegen::visit(TryStatement *ast)
+void Codegen::handleTryCatch(TryStatement *ast)
{
- if (hasError)
- return true;
-
- TempScope scope(_function);
-
- _function->hasTry = true;
-
- if (_function->isStrict && ast->catchExpression &&
- (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
+ Q_ASSERT(ast);
+ if (_context->isStrict &&
+ (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
- return false;
+ return;
}
- IR::BasicBlock *surroundingExceptionHandler = exceptionHandler();
-
- // We always need a finally body to clean up the exception handler
- // exceptions thrown in finally get caught by the surrounding catch block
- IR::BasicBlock *finallyBody = 0;
- IR::BasicBlock *catchBody = 0;
- IR::BasicBlock *catchExceptionHandler = 0;
- IR::BasicBlock *end = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock);
-
- if (ast->finallyExpression)
- finallyBody = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock);
-
- if (ast->catchExpression) {
- // exception handler for the catch body
- catchExceptionHandler = _function->newBasicBlock(0, IR::Function::DontInsertBlock);
- pushExceptionHandler(catchExceptionHandler);
- catchBody = _function->newBasicBlock(catchExceptionHandler, IR::Function::DontInsertBlock);
- popExceptionHandler();
- pushExceptionHandler(catchBody);
- } else {
- Q_ASSERT(finallyBody);
- pushExceptionHandler(finallyBody);
+ RegisterScope scope(this);
+ BytecodeGenerator::Label noException = bytecodeGenerator->newLabel();
+ {
+ ControlFlowCatch catchFlow(this, ast->catchExpression);
+ RegisterScope scope(this);
+ statement(ast->statement);
+ bytecodeGenerator->jump().link(noException);
}
+ noException.link();
+}
- IR::BasicBlock *tryBody = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(tryBody);
-
- ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression);
- _scopeAndFinally = &tcf;
-
- _block = tryBody;
- statement(ast->statement);
- _block->JUMP(finallyBody ? finallyBody : end);
-
- popExceptionHandler();
+void Codegen::handleTryFinally(TryStatement *ast)
+{
+ RegisterScope scope(this);
+ ControlFlowFinally finally(this, ast->finallyExpression);
if (ast->catchExpression) {
- pushExceptionHandler(catchExceptionHandler);
- _function->addBasicBlock(catchBody);
- _block = catchBody;
-
- ++_function->insideWithOrCatch;
- IR::ExprList *catchArgs = _function->New<IR::ExprList>();
- catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString())));
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchArgs));
- {
- ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope);
- _scopeAndFinally = &scope;
- statement(ast->catchExpression->statement);
- _scopeAndFinally = scope.parent;
- }
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- --_function->insideWithOrCatch;
- _block->JUMP(finallyBody ? finallyBody : end);
- popExceptionHandler();
-
- _function->addBasicBlock(catchExceptionHandler);
- catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- if (finallyBody || surroundingExceptionHandler)
- catchExceptionHandler->JUMP(finallyBody ? finallyBody : surroundingExceptionHandler);
- else
- catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
+ handleTryCatch(ast);
+ } else {
+ RegisterScope scope(this);
+ statement(ast->statement);
}
+}
- _scopeAndFinally = tcf.parent;
-
- if (finallyBody) {
- _function->addBasicBlock(finallyBody);
- _block = finallyBody;
-
- TempScope scope(_function);
+bool Codegen::visit(TryStatement *ast)
+{
+ if (hasError)
+ return true;
- int hasException = _block->newTemp();
- move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0));
+ Q_ASSERT(_context->hasTry);
- if (ast->finallyExpression && ast->finallyExpression->statement)
- statement(ast->finallyExpression->statement);
+ RegisterScope scope(this);
- IR::ExprList *arg = _function->New<IR::ExprList>();
- arg->expr = _block->TEMP(hasException);
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), arg));
- _block->JUMP(end);
+ if (ast->finallyExpression && ast->finallyExpression->statement) {
+ handleTryFinally(ast);
+ } else {
+ handleTryCatch(ast);
}
- _function->addBasicBlock(end);
- _block = end;
-
return false;
}
-void Codegen::unwindException(Codegen::ScopeAndFinally *outest)
-{
- int savedDepthForWidthOrCatch = _function->insideWithOrCatch;
- ScopeAndFinally *scopeAndFinally = _scopeAndFinally;
- qSwap(_scopeAndFinally, scopeAndFinally);
- while (_scopeAndFinally != outest) {
- switch (_scopeAndFinally->type) {
- case ScopeAndFinally::WithScope:
- // fall through
- case ScopeAndFinally::CatchScope:
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0)));
- _scopeAndFinally = _scopeAndFinally->parent;
- --_function->insideWithOrCatch;
- break;
- case ScopeAndFinally::TryScope: {
- ScopeAndFinally *tc = _scopeAndFinally;
- _scopeAndFinally = tc->parent;
- if (tc->finally && tc->finally->statement)
- statement(tc->finally->statement);
- break;
- }
- }
- }
- qSwap(_scopeAndFinally, scopeAndFinally);
- _function->insideWithOrCatch = savedDepthForWidthOrCatch;
-}
-
bool Codegen::visit(VariableStatement *ast)
{
if (hasError)
@@ -2896,23 +2741,25 @@ bool Codegen::visit(WhileStatement *ast)
if (hasError)
return true;
- IR::BasicBlock *whilecond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *whilebody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *whileend = _function->newBasicBlock(exceptionHandler());
+ if (AST::cast<FalseLiteral *>(ast->expression))
+ return false;
- enterLoop(ast, whileend, whilecond);
+ RegisterScope scope(this);
- _block->JUMP(whilecond);
- _block = whilecond;
- condition(ast->expression, whilebody, whileend);
+ BytecodeGenerator::Label start = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label cond = bytecodeGenerator->label();
+ ControlFlowLoop flow(this, &end, &cond);
- _block = whilebody;
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken);
+ if (!AST::cast<TrueLiteral *>(ast->expression))
+ condition(ast->expression, &start, &end, true);
- _block = whileend;
- leaveLoop();
+ start.link();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->whileToken);
+ bytecodeGenerator->jump().link(cond);
+ end.link();
return false;
}
@@ -2921,103 +2768,75 @@ bool Codegen::visit(WithStatement *ast)
if (hasError)
return true;
- TempScope scope(_function);
+ RegisterScope scope(this);
- _function->hasWith = true;
+ _context->hasWith = true;
- const int withObject = _block->newTemp();
- Result src = expression(ast->expression);
+ Reference src = expression(ast->expression);
if (hasError)
return false;
- _block->MOVE(_block->TEMP(withObject), *src);
-
- // need an exception handler for with to cleanup the with scope
- IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler());
- withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- if (!exceptionHandler())
- withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
- else
- withExceptionHandler->JUMP(exceptionHandler());
-
- pushExceptionHandler(withExceptionHandler);
-
- IR::BasicBlock *withBlock = _function->newBasicBlock(exceptionHandler());
+ src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
+ src.loadInAccumulator();
- _block->JUMP(withBlock);
- _block = withBlock;
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(withObject));
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args));
+ ControlFlowWith flow(this);
- ++_function->insideWithOrCatch;
- {
- ScopeAndFinally scope(_scopeAndFinally);
- _scopeAndFinally = &scope;
- statement(ast->statement);
- _scopeAndFinally = scope.parent;
- }
- --_function->insideWithOrCatch;
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- popExceptionHandler();
-
- IR::BasicBlock *next = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(next);
- _block = next;
+ statement(ast->statement);
return false;
}
bool Codegen::visit(UiArrayBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiObjectBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiObjectDefinition *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiPublicMember *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiScriptBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiSourceElement *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
-bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc)
+bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const SourceLocation& loc)
{
- if (!_variableEnvironment->isStrict)
- return false;
- if (IR::Name *n = expr->asName()) {
- if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments"))
- return false;
- } else if (IR::ArgLocal *al = expr->asArgLocal()) {
- if (!al->isArgumentsOrEval)
- return false;
- } else {
+ if (!_context->isStrict)
return false;
+ bool isArgOrEval = false;
+ if (r.type == Reference::Name) {
+ QString str = jsUnitGenerator->stringForIndex(r.nameAsIndex());
+ if (str == QLatin1String("eval") || str == QLatin1String("arguments")) {
+ isArgOrEval = true;
+ }
+ } else if (r.type == Reference::ScopedLocal || r.isRegister()) {
+ isArgOrEval = r.isArgOrEval;
}
- throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- return true;
+ if (isArgOrEval)
+ throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ return isArgOrEval;
}
void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
@@ -3049,6 +2868,116 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const
return _errors;
}
+QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData)
+{
+ CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit;
+ if (generateUnitData)
+ compilationUnit->data = jsUnitGenerator->generateUnit();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> unit;
+ unit.adopt(compilationUnit);
+ return unit;
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new CompiledData::CompilationUnit);
+ return result;
+}
+
+class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
+{
+ VolatileMemoryLocations locs;
+
+public:
+ Codegen::VolatileMemoryLocations scan(AST::Node *s)
+ {
+ s->accept(this);
+ return locs;
+ }
+
+ bool visit(ArrayMemberExpression *) Q_DECL_OVERRIDE
+ {
+ locs.setAllVolatile();
+ return false;
+ }
+
+ bool visit(FieldMemberExpression *) Q_DECL_OVERRIDE
+ {
+ locs.setAllVolatile();
+ return false;
+ }
+
+ bool visit(PostIncrementExpression *e) Q_DECL_OVERRIDE
+ {
+ collectIdentifiers(locs.specificLocations, e->base);
+ return false;
+ }
+
+ bool visit(PostDecrementExpression *e) Q_DECL_OVERRIDE
+ {
+ collectIdentifiers(locs.specificLocations, e->base);
+ return false;
+ }
+
+ bool visit(PreIncrementExpression *e) Q_DECL_OVERRIDE
+ {
+ collectIdentifiers(locs.specificLocations, e->expression);
+ return false;
+ }
+
+ bool visit(PreDecrementExpression *e) Q_DECL_OVERRIDE
+ {
+ collectIdentifiers(locs.specificLocations, e->expression);
+ return false;
+ }
+
+ bool visit(BinaryExpression *e) Q_DECL_OVERRIDE
+ {
+ switch (e->op) {
+ case QSOperator::InplaceAnd:
+ case QSOperator::InplaceSub:
+ case QSOperator::InplaceDiv:
+ case QSOperator::InplaceAdd:
+ case QSOperator::InplaceLeftShift:
+ case QSOperator::InplaceMod:
+ case QSOperator::InplaceMul:
+ case QSOperator::InplaceOr:
+ case QSOperator::InplaceRightShift:
+ case QSOperator::InplaceURightShift:
+ case QSOperator::InplaceXor:
+ collectIdentifiers(locs.specificLocations, e);
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+private:
+ void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
+ class Collector: public QQmlJS::AST::Visitor {
+ QVector<QStringView> &ids;
+ public:
+ Collector(QVector<QStringView> &ids): ids(ids) {}
+ virtual bool visit(IdentifierExpression *ie) {
+ ids.append(ie->name);
+ return false;
+ }
+ };
+ Collector collector(ids);
+ node->accept(&collector);
+ }
+};
+
+Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
+{
+ VolatileMemoryLocationScanner scanner;
+ return scanner.scan(ast);
+}
+
+
#ifndef V4_BOOTSTRAP
QList<QQmlError> Codegen::qmlErrors() const
@@ -3074,20 +3003,458 @@ QList<QQmlError> Codegen::qmlErrors() const
return qmlErrors;
}
-void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail)
+#endif // V4_BOOTSTRAP
+
+bool Codegen::RValue::operator==(const RValue &other) const
{
- if (hasError)
- return;
- hasError = true;
- engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn);
+ switch (type) {
+ case Accumulator:
+ return other.isAccumulator();
+ case StackSlot:
+ return other.isStackSlot() && theStackSlot == other.theStackSlot;
+ case Const:
+ return other.isConst() && constant == other.constant;
+ default:
+ return false;
+ }
}
-void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail)
+Codegen::RValue Codegen::RValue::storeOnStack() const
{
- if (hasError)
+ switch (type) {
+ case Accumulator:
+ return RValue::fromStackSlot(codegen, Reference::fromAccumulator(codegen).storeOnStack().stackSlot());
+ case StackSlot:
+ return *this;
+ case Const:
+ return RValue::fromStackSlot(codegen, Reference::storeConstOnStack(codegen, constant).stackSlot());
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+Codegen::Reference::Reference(const Codegen::Reference &other)
+{
+ *this = other;
+}
+
+Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
+{
+ type = other.type;
+
+ switch (type) {
+ case Invalid:
+ case Accumulator:
+ break;
+ case StackSlot:
+ theStackSlot = other.theStackSlot;
+ break;
+ case ScopedLocal:
+ index = other.index;
+ scope = other.scope;
+ break;
+ case Name:
+ name = other.name;
+ break;
+ case Member:
+ propertyBase = other.propertyBase;
+ propertyNameIndex = other.propertyNameIndex;
+ break;
+ case Subscript:
+ elementBase = other.elementBase;
+ elementSubscript = other.elementSubscript;
+ break;
+ case Const:
+ constant = other.constant;
+ break;
+ case QmlScopeObject:
+ case QmlContextObject:
+ qmlBase = other.qmlBase;
+ qmlCoreIndex = other.qmlCoreIndex;
+ qmlNotifyIndex = other.qmlNotifyIndex;
+ captureRequired = other.captureRequired;
+ break;
+ }
+
+ // keep loaded reference
+ isArgOrEval = other.isArgOrEval;
+ codegen = other.codegen;
+ isReadonly = other.isReadonly;
+ stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
+ isVolatile = other.isVolatile;
+ global = other.global;
+ return *this;
+}
+
+bool Codegen::Reference::operator==(const Codegen::Reference &other) const
+{
+ if (type != other.type)
+ return false;
+ switch (type) {
+ case Invalid:
+ case Accumulator:
+ break;
+ case StackSlot:
+ return theStackSlot == other.theStackSlot;
+ case ScopedLocal:
+ return index == other.index && scope == other.scope;
+ case Name:
+ return nameAsIndex() == other.nameAsIndex();
+ case Member:
+ return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex;
+ case Subscript:
+ return elementBase == other.elementBase && elementSubscript == other.elementSubscript;
+ case Const:
+ return constant == other.constant;
+ case QmlScopeObject:
+ case QmlContextObject:
+ return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex
+ && captureRequired == other.captureRequired;
+ }
+ return true;
+}
+
+Codegen::RValue Codegen::Reference::asRValue() const
+{
+ switch (type) {
+ case Invalid:
+ Q_UNREACHABLE();
+ case Accumulator:
+ return RValue::fromAccumulator(codegen);
+ case StackSlot:
+ return RValue::fromStackSlot(codegen, stackSlot());
+ case Const:
+ return RValue::fromConst(codegen, constant);
+ default:
+ loadInAccumulator();
+ return RValue::fromAccumulator(codegen);
+ }
+}
+
+Codegen::Reference Codegen::Reference::asLValue() const
+{
+ switch (type) {
+ case Invalid:
+ case Accumulator:
+ Q_UNREACHABLE();
+ case Member:
+ if (!propertyBase.isStackSlot()) {
+ Reference r = *this;
+ r.propertyBase = propertyBase.storeOnStack();
+ return r;
+ }
+ return *this;
+ case Subscript:
+ if (!elementSubscript.isStackSlot()) {
+ Reference r = *this;
+ r.elementSubscript = elementSubscript.storeOnStack();
+ return r;
+ }
+ return *this;
+ default:
+ return *this;
+ }
+}
+
+Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
+{
+ storeAccumulator(); // it doesn't matter what happens here, just do it.
+ return Reference();
+}
+
+Codegen::Reference Codegen::Reference::storeOnStack() const
+{ return doStoreOnStack(-1); }
+
+void Codegen::Reference::storeOnStack(int slotIndex) const
+{ doStoreOnStack(slotIndex); }
+
+Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
+{
+ if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile))
+ return *this;
+
+ if (isStackSlot()) { // temp-to-temp move
+ Reference dest = Reference::fromStackSlot(codegen, slotIndex);
+ Instruction::MoveReg move;
+ move.srcReg = stackSlot();
+ move.destReg = dest.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(move);
+ return dest;
+ }
+
+ Reference slot = Reference::fromStackSlot(codegen, slotIndex);
+ if (isConst()) {
+ Instruction::MoveConst move;
+ move.constIndex = codegen->registerConstant(constant);
+ move.destTemp = slot.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(move);
+ } else {
+ loadInAccumulator();
+ slot.storeConsumeAccumulator();
+ }
+ return slot;
+}
+
+Codegen::Reference Codegen::Reference::storeRetainAccumulator() const
+{
+ if (storeWipesAccumulator()) {
+ // a store will
+ auto tmp = Reference::fromStackSlot(codegen);
+ tmp.storeAccumulator(); // this is safe, and won't destory the accumulator
+ storeAccumulator();
+ return tmp;
+ } else {
+ // ok, this is safe, just do the store.
+ storeAccumulator();
+ return *this;
+ }
+}
+
+bool Codegen::Reference::storeWipesAccumulator() const
+{
+ switch (type) {
+ default:
+ case Invalid:
+ case Const:
+ case Accumulator:
+ Q_UNREACHABLE();
+ return false;
+ case StackSlot:
+ case ScopedLocal:
+ return false;
+ case Name:
+ case Member:
+ case Subscript:
+ case QmlScopeObject:
+ case QmlContextObject:
+ return true;
+ }
+}
+
+void Codegen::Reference::storeAccumulator() const
+{
+ switch (type) {
+ case StackSlot: {
+ Instruction::StoreReg store;
+ store.reg = theStackSlot;
+ codegen->bytecodeGenerator->addInstruction(store);
return;
- hasError = true;
- engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn);
+ }
+ case ScopedLocal: {
+ if (scope == 0) {
+ Instruction::StoreLocal store;
+ store.index = index;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreScopedLocal store;
+ store.index = index;
+ store.scope = scope;
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ return;
+ }
+ case Name: {
+ Context *c = codegen->currentContext();
+ if (c->isStrict) {
+ Instruction::StoreNameStrict store;
+ store.name = nameAsIndex();
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreNameSloppy store;
+ store.name = nameAsIndex();
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ } return;
+ case Member:
+ if (codegen->useFastLookups) {
+ Instruction::SetLookup store;
+ store.base = propertyBase.stackSlot();
+ store.index = codegen->registerSetterLookup(propertyNameIndex);
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreProperty store;
+ store.base = propertyBase.stackSlot();
+ store.name = propertyNameIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ return;
+ case Subscript: {
+ Instruction::StoreElement store;
+ store.base = elementBase;
+ store.index = elementSubscript.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(store);
+ } return;
+ case QmlScopeObject: {
+ Instruction::StoreScopeObjectProperty store;
+ store.base = qmlBase;
+ store.propertyIndex = qmlCoreIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } return;
+ case QmlContextObject: {
+ Instruction::StoreContextObjectProperty store;
+ store.base = qmlBase;
+ store.propertyIndex = qmlCoreIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } return;
+ case Invalid:
+ case Accumulator:
+ case Const:
+ break;
+ }
+
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
}
-#endif // V4_BOOTSTRAP
+void Codegen::Reference::loadInAccumulator() const
+{
+ switch (type) {
+ case Accumulator:
+ return;
+ case Const: {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (constant == Encode::null()) {
+ Instruction::LoadNull load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode(true)) {
+ Instruction::LoadTrue load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode(false)) {
+ Instruction::LoadFalse load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode::undefined()) {
+ Instruction::LoadUndefined load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Value p = Primitive::fromReturnedValue(constant);
+ if (p.isNumber()) {
+ double d = p.asDouble();
+ int i = static_cast<int>(d);
+ if (d == i && (d != 0 || !std::signbit(d))) {
+ if (!i) {
+ Instruction::LoadZero load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
+ }
+ Instruction::LoadInt load;
+ load.value = Primitive::fromReturnedValue(constant).toInt32();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
+ }
+ }
+ Instruction::LoadConst load;
+ load.index = codegen->registerConstant(constant);
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+QT_WARNING_POP
+ } return;
+ case StackSlot: {
+ Instruction::LoadReg load;
+ load.reg = stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ } return;
+ case ScopedLocal: {
+ if (!scope) {
+ Instruction::LoadLocal load;
+ load.index = index;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Instruction::LoadScopedLocal load;
+ load.index = index;
+ load.scope = scope;
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+ return;
+ }
+ case Name:
+ if (global) {
+ // these value properties of the global object are immutable, we we can directly convert them
+ // to their numeric value here
+ if (name == QStringLiteral("undefined")) {
+ Reference::fromConst(codegen, Encode::undefined()).loadInAccumulator();
+ return;
+ } else if (name == QStringLiteral("Infinity")) {
+ Reference::fromConst(codegen, Encode(qInf())).loadInAccumulator();
+ return;
+ } else if (name == QStringLiteral("Nan")) {
+ Reference::fromConst(codegen, Encode(qQNaN())).loadInAccumulator();
+ return;
+ }
+ }
+ if (codegen->useFastLookups && global) {
+ Instruction::LoadGlobalLookup load;
+ load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Instruction::LoadName load;
+ load.name = nameAsIndex();
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+ return;
+ case Member:
+ if (codegen->useFastLookups) {
+ if (propertyBase.isAccumulator()) {
+ Instruction::GetLookupA load;
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Instruction::GetLookup load;
+ load.base = propertyBase.storeOnStack().stackSlot();
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+ } else {
+ if (propertyBase.isAccumulator()) {
+ Instruction::LoadPropertyA load;
+ load.name = propertyNameIndex;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Instruction::LoadProperty load;
+ load.base = propertyBase.storeOnStack().stackSlot();
+ load.name = propertyNameIndex;
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+ }
+ return;
+ case Subscript: {
+ if (elementSubscript.isAccumulator()) {
+ Instruction::LoadElementA load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (elementSubscript.isConst()) {
+ Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator();
+ Instruction::LoadElementA load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Instruction::LoadElement load;
+ load.base = elementBase;
+ load.index = elementSubscript.storeOnStack().stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+ } return;
+ case QmlScopeObject: {
+ Instruction::LoadScopeObjectProperty load;
+ load.base = qmlBase;
+ load.propertyIndex = qmlCoreIndex;
+ load.captureRequired = captureRequired;
+ codegen->bytecodeGenerator->addInstruction(load);
+ if (!captureRequired)
+ codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
+ } return;
+ case QmlContextObject: {
+ Instruction::LoadContextObjectProperty load;
+ load.base = qmlBase;
+ load.propertyIndex = qmlCoreIndex;
+ load.captureRequired = captureRequired;
+ codegen->bytecodeGenerator->addInstruction(load);
+ if (!captureRequired)
+ codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
+ } return;
+ case Invalid:
+ break;
+ }
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
+}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 31e9cc0f46..dba9388292 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -51,311 +51,465 @@
//
#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
+#include <private/qv4instr_moth_p.h>
+#include <private/qv4compiler_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qqmlrefcount_p.h>
#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
#include <QStack>
#ifndef V4_BOOTSTRAP
#include <qqmlerror.h>
#endif
#include <private/qv4util_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace AST {
-class UiParameterList;
+using namespace QQmlJS;
+
+namespace QV4 {
+
+namespace Moth {
+struct Instruction;
}
+namespace CompiledData {
+struct CompilationUnit;
+}
+
+namespace Compiler {
+
+struct ControlFlow;
+struct ControlFlowCatch;
+struct ControlFlowFinally;
-class Q_QML_PRIVATE_EXPORT Codegen: protected AST::Visitor
+class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
{
+protected:
+ using BytecodeGenerator = QV4::Moth::BytecodeGenerator;
+ using Instruction = QV4::Moth::Instruction;
public:
- Codegen(bool strict);
-
- enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
- // * function declarations are moved to the return address when encountered
- // * return statements are allowed everywhere (like in FunctionCode)
- // * variable declarations are treated as true locals (like in FunctionCode)
- };
+ Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict);
+
void generateFromProgram(const QString &fileName,
const QString &sourceCode,
AST::Program *ast,
- QV4::IR::Module *module,
- CompilationMode mode = GlobalCode,
- const QStringList &inheritedLocals = QStringList());
- void generateFromFunctionExpression(const QString &fileName,
- const QString &sourceCode,
- AST::FunctionExpression *ast,
- QV4::IR::Module *module);
+ Module *module,
+ CompilationMode mode = GlobalCode);
-protected:
- enum Format { ex, cx, nx };
- struct Result {
- QV4::IR::Expr *code;
- QV4::IR::BasicBlock *iftrue;
- QV4::IR::BasicBlock *iffalse;
- Format format;
- Format requested;
+public:
+ class VolatileMemoryLocationScanner;
+ class VolatileMemoryLocations {
+ friend VolatileMemoryLocationScanner;
+ bool allVolatile = false;
+ QVector<QStringView> specificLocations;
+ public:
+ bool isVolatile(const QStringView &name) {
+ if (allVolatile)
+ return true;
+ return specificLocations.contains(name);
+ }
- explicit Result(Format requested = ex)
- : code(0)
- , iftrue(0)
- , iffalse(0)
- , format(ex)
- , requested(requested) {}
-
- explicit Result(QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse)
- : code(0)
- , iftrue(iftrue)
- , iffalse(iffalse)
- , format(ex)
- , requested(cx) {}
-
- inline QV4::IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
- inline QV4::IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; }
+ void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); }
+ void setAllVolatile() { allVolatile = true; }
+ };
+ class RValue {
+ Codegen *codegen;
+ enum Type {
+ Invalid,
+ Accumulator,
+ StackSlot,
+ Const
+ } type;
+ union {
+ Moth::StackSlot theStackSlot;
+ QV4::ReturnedValue constant;
+ };
- bool accept(Format f)
- {
- if (requested == f) {
- format = f;
+ public:
+ static RValue fromStackSlot(Codegen *codegen, Moth::StackSlot stackSlot) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = StackSlot;
+ r.theStackSlot = stackSlot;
+ return r;
+ }
+ static RValue fromAccumulator(Codegen *codegen) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = Accumulator;
+ return r;
+ }
+ static RValue fromConst(Codegen *codegen, QV4::ReturnedValue value) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = Const;
+ r.constant = value;
+ return r;
+ }
+
+ bool operator==(const RValue &other) const;
+
+ bool isValid() const { return type != Invalid; }
+ bool isAccumulator() const { return type == Accumulator; }
+ bool isStackSlot() const { return type == StackSlot; }
+ bool isConst() const { return type == Const; }
+
+ Moth::StackSlot stackSlot() const {
+ Q_ASSERT(isStackSlot());
+ return theStackSlot;
+ }
+
+ QV4::ReturnedValue constantValue() const {
+ Q_ASSERT(isConst());
+ return constant;
+ }
+
+ Q_REQUIRED_RESULT RValue storeOnStack() const;
+ };
+ struct Reference {
+ enum Type {
+ Invalid,
+ Accumulator,
+ StackSlot,
+ ScopedLocal,
+ Name,
+ Member,
+ Subscript,
+ QmlScopeObject,
+ QmlContextObject,
+ LastLValue = QmlContextObject,
+ Const
+ } type = Invalid;
+
+ bool isLValue() const { return !isReadonly; }
+
+ Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {}
+ Reference()
+ : type(Invalid)
+ , codegen(nullptr)
+ {}
+ Reference(const Reference &other);
+
+ Reference &operator =(const Reference &other);
+
+ bool operator==(const Reference &other) const;
+
+ bool isValid() const { return type != Invalid; }
+ bool loadTriggersSideEffect() const {
+ switch (type) {
+ case Name:
+ case Member:
+ case Subscript:
return true;
+ default:
+ return false;
}
- return false;
}
- };
+ bool isConst() const { return type == Const; }
+ bool isAccumulator() const { return type == Accumulator; }
+ bool isStackSlot() const { return type == StackSlot; }
+ bool isRegister() const {
+ return isStackSlot();
+ }
- struct Environment {
- Environment *parent;
+ static Reference fromAccumulator(Codegen *cg) {
+ return Reference(cg, Accumulator);
+ }
+ static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) {
+ Reference r(cg, StackSlot);
+ if (tempIndex == -1)
+ tempIndex = cg->bytecodeGenerator->newRegister();
+ r.theStackSlot = Moth::StackSlot::createRegister(tempIndex);
+ r.stackSlotIsLocalOrArgument = isLocal;
+ return r;
+ }
+ static Reference fromArgument(Codegen *cg, int index, bool isVolatile) {
+ Reference r(cg, StackSlot);
+ r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1);
+ r.stackSlotIsLocalOrArgument = true;
+ r.isVolatile = isVolatile;
+ return r;
+ }
+ static Reference fromScopedLocal(Codegen *cg, int index, int scope) {
+ Reference r(cg, ScopedLocal);
+ r.index = index;
+ r.scope = scope;
+ return r;
+ }
+ static Reference fromName(Codegen *cg, const QString &name) {
+ Reference r(cg, Name);
+ r.name = name;
+ return r;
+ }
+ static Reference fromMember(const Reference &baseRef, const QString &name) {
+ Reference r(baseRef.codegen, Member);
+ r.propertyBase = baseRef.asRValue();
+ r.propertyNameIndex = r.codegen->registerString(name);
+ return r;
+ }
+ static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
+ Q_ASSERT(baseRef.isStackSlot());
+ Reference r(baseRef.codegen, Subscript);
+ r.elementBase = baseRef.stackSlot();
+ r.elementSubscript = subscript.asRValue();
+ return r;
+ }
+ static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) {
+ Reference r(cg, Const);
+ r.constant = constant;
+ r.isReadonly = true;
+ return r;
+ }
+ static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) {
+ Reference r(base.codegen, QmlScopeObject);
+ r.qmlBase = base.storeOnStack().stackSlot();
+ r.qmlCoreIndex = coreIndex;
+ r.qmlNotifyIndex = notifyIndex;
+ r.captureRequired = captureRequired;
+ return r;
+ }
+ static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, bool captureRequired) {
+ Reference r(base.codegen, QmlContextObject);
+ r.qmlBase = base.storeOnStack().stackSlot();
+ r.qmlCoreIndex = coreIndex;
+ r.qmlNotifyIndex = notifyIndex;
+ r.captureRequired = captureRequired;
+ return r;
+ }
+ static Reference fromThis(Codegen *cg) {
+ Reference r = fromStackSlot(cg, CallData::This);
+ r.isReadonly = true;
+ return r;
+ }
- enum MemberType {
- UndefinedMember,
- VariableDefinition,
- VariableDeclaration,
- FunctionDefinition
- };
+ RValue asRValue() const;
+ Reference asLValue() const;
- struct Member {
- MemberType type;
- int index;
- AST::FunctionExpression *function;
- AST::VariableDeclaration::VariableScope scope;
+ Q_REQUIRED_RESULT static Reference storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant)
+ { return Reference::fromConst(cg, constant).storeOnStack(); }
- bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; }
- };
- typedef QMap<QString, Member> MemberMap;
-
- MemberMap members;
- AST::FormalParameterList *formals;
- int maxNumberOfArguments;
- bool hasDirectEval;
- bool hasNestedFunctions;
- bool isStrict;
- bool isNamedFunctionExpression;
- bool usesThis;
- enum UsesArgumentsObject {
- ArgumentsObjectUnknown,
- ArgumentsObjectNotUsed,
- ArgumentsObjectUsed
+ static void storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant, int stackSlot)
+ { Reference::fromConst(cg, constant).storeOnStack(stackSlot); }
+
+ Q_REQUIRED_RESULT Reference storeOnStack() const;
+ void storeOnStack(int tempIndex) const;
+ Q_REQUIRED_RESULT Reference storeRetainAccumulator() const;
+ Reference storeConsumeAccumulator() const;
+
+ bool storeWipesAccumulator() const;
+ void loadInAccumulator() const;
+
+ int nameAsIndex() const {
+ Q_ASSERT(type == Name);
+ return codegen->registerString(name);
+ }
+
+ Moth::StackSlot stackSlot() const {
+ if (Q_UNLIKELY(!isStackSlot()))
+ Q_UNREACHABLE();
+ return theStackSlot;
+ }
+
+ union {
+ Moth::StackSlot theStackSlot;
+ QV4::ReturnedValue constant;
+ struct { // Scoped arguments/Local
+ int index;
+ int scope;
+ };
+ struct {
+ RValue propertyBase;
+ int propertyNameIndex;
+ };
+ struct {
+ Moth::StackSlot elementBase;
+ RValue elementSubscript;
+ };
+ struct { // QML scope/context object case
+ Moth::StackSlot qmlBase;
+ qint16 qmlCoreIndex;
+ qint16 qmlNotifyIndex;
+ bool captureRequired;
+ };
};
+ QString name;
+ mutable bool isArgOrEval = false;
+ bool isReadonly = false;
+ bool stackSlotIsLocalOrArgument = false;
+ bool isVolatile = false;
+ bool global = false;
+ Codegen *codegen;
+
+ private:
+ void storeAccumulator() const;
+ Reference doStoreOnStack(int tempIndex) const;
+ };
- UsesArgumentsObject usesArgumentsObject;
-
- CompilationMode compilationMode;
-
- Environment(Environment *parent, CompilationMode mode)
- : parent(parent)
- , formals(0)
- , maxNumberOfArguments(0)
- , hasDirectEval(false)
- , hasNestedFunctions(false)
- , isStrict(false)
- , isNamedFunctionExpression(false)
- , usesThis(false)
- , usesArgumentsObject(ArgumentsObjectUnknown)
- , compilationMode(mode)
- {
- if (parent && parent->isStrict)
- isStrict = true;
+ struct RegisterScope {
+ RegisterScope(Codegen *cg)
+ : generator(cg->bytecodeGenerator),
+ regCountForScope(generator->currentReg) {}
+ ~RegisterScope() {
+ generator->currentReg = regCountForScope;
}
+ BytecodeGenerator *generator;
+ int regCountForScope;
+ };
+
+ struct ObjectPropertyValue {
+ ObjectPropertyValue()
+ : getter(-1)
+ , setter(-1)
+ , keyAsIndex(UINT_MAX)
+ {}
+
+ Reference rvalue;
+ int getter; // index in _module->functions or -1 if not set
+ int setter;
+ uint keyAsIndex;
+
+ bool hasGetter() const { return getter >= 0; }
+ bool hasSetter() const { return setter >= 0; }
+ };
+protected:
- int findMember(const QString &name) const
+ enum Format { ex, cx, nx };
+ class Result {
+ Reference _result;
+
+ const BytecodeGenerator::Label *_iftrue;
+ const BytecodeGenerator::Label *_iffalse;
+ Format _format;
+ Format _requested;
+ bool _trueBlockFollowsCondition = false;
+
+ public:
+ explicit Result(const Reference &lrvalue)
+ : _result(lrvalue)
+ , _iftrue(nullptr)
+ , _iffalse(nullptr)
+ , _format(ex)
+ , _requested(ex)
{
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end())
- return -1;
- Q_ASSERT((*it).index != -1 || !parent);
- return (*it).index;
}
- bool memberInfo(const QString &name, const Member **m) const
+ explicit Result(Format requested = ex)
+ : _iftrue(0)
+ , _iffalse(0)
+ , _format(ex)
+ , _requested(requested) {}
+
+ explicit Result(const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse,
+ bool trueBlockFollowsCondition)
+ : _iftrue(iftrue)
+ , _iffalse(iffalse)
+ , _format(ex)
+ , _requested(cx)
+ , _trueBlockFollowsCondition(trueBlockFollowsCondition)
{
- Q_ASSERT(m);
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end()) {
- *m = 0;
- return false;
- }
- *m = &(*it);
- return true;
+ Q_ASSERT(iftrue);
+ Q_ASSERT(iffalse);
}
- bool lookupMember(const QString &name, Environment **scope, int *index, int *distance)
- {
- Environment *it = this;
- *distance = 0;
- for (; it; it = it->parent, ++(*distance)) {
- int idx = it->findMember(name);
- if (idx != -1) {
- *scope = it;
- *index = idx;
- return true;
- }
- }
- return false;
+ const BytecodeGenerator::Label *iftrue() const {
+ Q_ASSERT(_requested == cx);
+ return _iftrue;
+ }
+
+ const BytecodeGenerator::Label *iffalse() const {
+ Q_ASSERT(_requested == cx);
+ return _iffalse;
+ }
+
+ Format format() const {
+ return _format;
}
- void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0)
+ bool accept(Format f)
{
- if (! name.isEmpty()) {
- if (type != FunctionDefinition) {
- for (AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return;
- }
- MemberMap::iterator it = members.find(name);
- if (it == members.end()) {
- Member m;
- m.index = -1;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- } else {
- Q_ASSERT(scope == (*it).scope);
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- }
+ if (_requested == f) {
+ _format = f;
+ return true;
}
+ return false;
}
- };
- struct TempScope {
- TempScope(QV4::IR::Function *f)
- : function(f),
- tempCountForScope(f->currentTemp) {}
- ~TempScope() {
- function->currentTemp = tempCountForScope;
+ bool trueBlockFollowsCondition() const {
+ return _trueBlockFollowsCondition;
}
- QV4::IR::Function *function;
- int tempCountForScope;
- };
- Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode)
- {
- Environment *env = new Environment(parent, compilationMode);
- _envMap.insert(node, env);
- return env;
- }
+ const Reference &result() const {
+ return _result;
+ }
- struct UiMember {
+ void setResult(const Reference &result) {
+ _result = result;
+ }
};
- struct ScopeAndFinally {
- enum ScopeType {
- WithScope,
- TryScope,
- CatchScope
- };
+ void enterContext(AST::Node *node);
+ int leaveContext();
- ScopeAndFinally *parent;
- AST::Finally *finally;
- ScopeType type;
+ void leaveLoop();
- ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), type(t) {}
- ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally)
- : parent(parent), finally(finally), type(TryScope)
- {}
+ enum UnaryOperation {
+ UPlus,
+ UMinus,
+ PreIncrement,
+ PreDecrement,
+ PostIncrement,
+ PostDecrement,
+ Not,
+ Compl
};
- struct Loop {
- AST::LabelledStatement *labelledStatement;
- AST::Statement *node;
- QV4::IR::BasicBlock *breakBlock;
- QV4::IR::BasicBlock *continueBlock;
- Loop *parent;
- ScopeAndFinally *scopeAndFinally;
-
- Loop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock, Loop *parent)
- : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {}
- };
+ Reference unop(UnaryOperation op, const Reference &expr);
- void enterEnvironment(AST::Node *node);
- void leaveEnvironment();
+ void addCJump();
- void enterLoop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock);
- void leaveLoop();
- QV4::IR::BasicBlock *exceptionHandler() const
- {
- if (_exceptionHandlers.isEmpty())
- return 0;
- return _exceptionHandlers.top();
- }
- void pushExceptionHandler(QV4::IR::BasicBlock *handler)
- {
- handler->setExceptionHandler(true);
- _exceptionHandlers.push(handler);
+ int registerString(const QString &name) {
+ return jsUnitGenerator->registerString(name);
}
- void popExceptionHandler()
- {
- Q_ASSERT(!_exceptionHandlers.isEmpty());
- _exceptionHandlers.pop();
- }
-
- QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name);
- QV4::IR::Expr *argument(QV4::IR::Expr *expr);
- QV4::IR::Expr *reference(QV4::IR::Expr *expr);
- QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation());
- QV4::IR::Expr *binop(QV4::IR::AluOp op, QV4::IR::Expr *left, QV4::IR::Expr *right, const AST::SourceLocation &loc = AST::SourceLocation());
- QV4::IR::Expr *call(QV4::IR::Expr *base, QV4::IR::ExprList *args);
- QV4::IR::Stmt *move(QV4::IR::Expr *target, QV4::IR::Expr *source, QV4::IR::AluOp op = QV4::IR::OpInvalid);
- QV4::IR::Stmt *cjump(QV4::IR::Expr *cond, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse);
+ int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); }
+ int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); }
+ int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); }
+ int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); }
// Returns index in _module->functions
- int defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::SourceElements *body,
- const QStringList &inheritedLocals = QStringList());
-
- void unwindException(ScopeAndFinally *outest);
+ virtual int defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::SourceElements *body);
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
- void condition(AST::ExpressionNode *ast, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse);
- Result expression(AST::ExpressionNode *ast);
+ void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse,
+ bool trueBlockFollowsCondition);
+ Reference expression(AST::ExpressionNode *ast);
Result sourceElement(AST::SourceElement *ast);
- UiMember uiObjectMember(AST::UiObjectMember *ast);
void accept(AST::Node *node);
void functionBody(AST::FunctionBody *ast);
void program(AST::Program *ast);
void sourceElements(AST::SourceElements *ast);
+ void statementList(AST::StatementList *ast);
void variableDeclaration(AST::VariableDeclaration *ast);
void variableDeclarationList(AST::VariableDeclarationList *ast);
- QV4::IR::Expr *identifier(const QString &name, int line = 0, int col = 0);
+ Reference referenceForName(const QString &name, bool lhs);
+
+ void loadClosure(int index);
+
// Hook provided to implement QML lookup semantics
- virtual QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col);
+ virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
// nodes
@@ -457,7 +611,7 @@ protected:
bool visit(AST::UiScriptBinding *ast) override;
bool visit(AST::UiSourceElement *ast) override;
- bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(QV4::IR::Expr* expr, const AST::SourceLocation &loc);
+ bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
@@ -467,117 +621,47 @@ public:
QList<QQmlError> qmlErrors() const;
#endif
+ Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
+ Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
+ struct Arguments { int argc; int argv; };
+ Arguments pushArgs(AST::ArgumentList *args);
+
+ void setUseFastLookups(bool b) { useFastLookups = b; }
+
+ void handleTryCatch(AST::TryStatement *ast);
+ void handleTryFinally(AST::TryStatement *ast);
+
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
+
+ Context *currentContext() const { return _context; }
+
protected:
+ friend class ScanFunctions;
+ friend struct ControlFlow;
+ friend struct ControlFlowCatch;
+ friend struct ControlFlowFinally;
Result _expr;
- QString _property;
- UiMember _uiMember;
- QV4::IR::Module *_module;
- QV4::IR::Function *_function;
- QV4::IR::BasicBlock *_block;
- QV4::IR::BasicBlock *_exitBlock;
- unsigned _returnAddress;
- Environment *_variableEnvironment;
- Loop *_loop;
+ VolatileMemoryLocations _volataleMemoryLocations;
+ Module *_module;
+ int _returnAddress;
+ Context *_context;
AST::LabelledStatement *_labelledStatement;
- ScopeAndFinally *_scopeAndFinally;
- QHash<AST::Node *, Environment *> _envMap;
- QHash<AST::FunctionExpression *, int> _functionMap;
- QStack<QV4::IR::BasicBlock *> _exceptionHandlers;
+ QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
+ BytecodeGenerator *bytecodeGenerator = 0;
bool _strictMode;
+ bool useFastLookups = true;
+ bool requiresReturnValue = false;
bool _fileNameIsUrl;
bool hasError;
QList<QQmlJS::DiagnosticMessage> _errors;
- class ScanFunctions: protected Visitor
- {
- typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
- public:
- ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
- void operator()(AST::Node *node);
-
- void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
- void leaveEnvironment();
-
- void enterQmlScope(AST::Node *ast, const QString &name)
- { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); }
-
- void enterQmlFunction(AST::FunctionDeclaration *ast)
- { enterFunction(ast, false, false); }
-
- protected:
- using Visitor::visit;
- using Visitor::endVisit;
-
- void checkDirectivePrologue(AST::SourceElements *ast);
-
- void checkName(const QStringRef &name, const AST::SourceLocation &loc);
- void checkForArguments(AST::FormalParameterList *parameters);
-
- bool visit(AST::Program *ast) override;
- void endVisit(AST::Program *) override;
-
- bool visit(AST::CallExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
- bool visit(AST::IdentifierExpression *ast) override;
- bool visit(AST::ExpressionStatement *ast) override;
- bool visit(AST::FunctionExpression *ast) override;
-
- void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true);
-
- void endVisit(AST::FunctionExpression *) override;
-
- bool visit(AST::ObjectLiteral *ast) override;
-
- bool visit(AST::PropertyGetterSetter *ast) override;
- void endVisit(AST::PropertyGetterSetter *) override;
-
- bool visit(AST::FunctionDeclaration *ast) override;
- void endVisit(AST::FunctionDeclaration *) override;
-
- bool visit(AST::WithStatement *ast) override;
-
- bool visit(AST::DoWhileStatement *ast) override;
- bool visit(AST::ForStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
- bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::ThisExpression *ast) override;
-
- bool visit(AST::Block *ast) override;
-
- protected:
- void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression);
-
- // fields:
- Codegen *_cg;
- const QString _sourceCode;
- Environment *_variableEnvironment;
- QStack<Environment *> _envStack;
-
- bool _allowFuncDecls;
- CompilationMode defaultProgramMode;
- };
-
-};
-
-#ifndef V4_BOOTSTRAP
-class RuntimeCodegen : public Codegen
-{
-public:
- RuntimeCodegen(QV4::ExecutionEngine *engine, bool strict)
- : Codegen(strict)
- , engine(engine)
- {}
-
- void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
- void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
private:
- QV4::ExecutionEngine *engine;
+ VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
};
-#endif // V4_BOOTSTRAP
+
+}
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 292f5cde45..3a1fd7fce5 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4compileddata_p.h"
-#include "qv4jsir_p.h"
#include <private/qv4value_p.h>
#ifndef V4_BOOTSTRAP
#include <private/qv4engine_p.h>
@@ -50,6 +49,7 @@
#include <private/qqmlpropertycache_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qv4vme_moth_p.h>
#include "qv4compilationunitmapper_p.h"
#include <QQmlPropertyMap>
#include <QDateTime>
@@ -94,23 +94,10 @@ static QString cacheFilePath(const QUrl &url)
}
#endif
-#ifndef V4_BOOTSTRAP
CompilationUnit::CompilationUnit()
- : data(0)
- , engine(0)
- , qmlEngine(0)
- , runtimeLookups(0)
- , runtimeRegularExpressions(0)
- , runtimeClasses(0)
- , constants(nullptr)
- , totalBindingsCount(0)
- , totalParserStatusCount(0)
- , totalObjectCount(0)
- , metaTypeId(-1)
- , listMetaTypeId(-1)
- , isRegisteredWithEngine(false)
{}
+#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
{
unlink();
@@ -163,10 +150,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->setter = QV4::Lookup::setterGeneric;
else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
- else if (type == CompiledData::Lookup::Type_IndexedGetter)
- l->indexedGetter = QV4::Lookup::indexedGetterGeneric;
- else if (type == CompiledData::Lookup::Type_IndexedSetter)
- l->indexedSetter = QV4::Lookup::indexedSetterGeneric;
for (int j = 0; j < QV4::Lookup::Size; ++j)
l->classList[j] = 0;
@@ -201,6 +184,19 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
linkBackendToEngine(engine);
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Constant table";
+ Moth::dumpConstantTable(constants, data->constantTableSize);
+ qDebug() << "=== String table";
+ for (uint i = 0; i < data->stringTableSize; ++i)
+ qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
+ qDebug() << "=== Closure table";
+ for (uint i = 0; i < data->functionTableSize; ++i)
+ qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
+ qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0);
+ }
+
if (data->indexOfRootFunction != -1)
return runtimeFunctions[data->indexOfRootFunction];
else
@@ -260,14 +256,6 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack)
}
}
-void CompilationUnit::destroy()
-{
- if (qmlEngine)
- QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
- else
- delete this;
-}
-
IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
{
auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
@@ -349,7 +337,7 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe
sizeof(data->dependencyMD5Checksum)) == 0;
}
-bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString)
+bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
{
if (!QQmlFile::isLocalFile(url)) {
*errorString = QStringLiteral("File has to be a local file.");
@@ -382,26 +370,26 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
{
const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex);
- const QString expectedCodeGenerator = iselFactory->codeGeneratorName;
+ const QString expectedCodeGenerator = QStringLiteral("moth"); // ###
if (foundCodeGenerator != expectedCodeGenerator) {
*errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator);
return false;
}
}
- if (!memoryMapCode(errorString))
- return false;
-
dataPtrChange.commit();
free(const_cast<Unit*>(oldDataPtr));
backingFile.reset(cacheFile.take());
return true;
}
-bool CompilationUnit::memoryMapCode(QString *errorString)
+void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
{
- *errorString = QStringLiteral("Missing code mapping backend");
- return false;
+ runtimeFunctions.resize(data->functionTableSize);
+ for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
+ const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
+ runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec);
+ }
}
#endif // V4_BOOTSTRAP
@@ -443,17 +431,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
unitPtr->flags |= Unit::StaticData;
- prepareCodeOffsetsForDiskStorage(unitPtr);
-
qint64 headerWritten = cacheFile.write(modifiedUnit);
if (headerWritten != modifiedUnit.size()) {
*errorString = cacheFile.errorString();
return false;
}
- if (!saveCodeToDisk(&cacheFile, unitPtr, errorString))
- return false;
-
if (!cacheFile.commit()) {
*errorString = cacheFile.errorString();
return false;
@@ -467,19 +450,6 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
#endif // QT_CONFIG(temporaryfile)
}
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
-{
- Q_UNUSED(unit);
-}
-
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString)
-{
- Q_UNUSED(device);
- Q_UNUSED(unit);
- *errorString = QStringLiteral("Saving code to disk is not supported in this configuration");
- return false;
-}
-
Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
{
if (!irDocument->javaScriptCompilationUnit->data)
@@ -774,6 +744,17 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e
#endif
+void CompilationUnit::destroy()
+{
+#if !defined(V4_BOOTSTRAP)
+ if (qmlEngine)
+ QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
+ else
+#endif
+ delete this;
+}
+
+
void Unit::generateChecksum()
{
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index a83dea1a0a..55c5f1f29f 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -72,7 +72,7 @@
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x13
+#define QV4_DATA_STRUCTURE_VERSION 0x15
class QIODevice;
class QQmlPropertyCache;
@@ -87,9 +87,6 @@ struct Document;
}
namespace QV4 {
-namespace IR {
-struct Function;
-}
struct Function;
class EvalISelFactory;
@@ -155,9 +152,7 @@ struct Lookup
enum Type : unsigned int {
Type_Getter = 0x0,
Type_Setter = 0x1,
- Type_GlobalGetter = 2,
- Type_IndexedGetter = 3,
- Type_IndexedSetter = 4
+ Type_GlobalGetter = 2
};
union {
@@ -202,6 +197,11 @@ struct String
};
static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct CodeOffsetToLine {
+ quint32_le codeOffset;
+ quint32_le line;
+};
+
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
@@ -210,22 +210,23 @@ struct Function
IsStrict = 0x1,
HasDirectEval = 0x2,
UsesArgumentsObject = 0x4,
- IsNamedExpression = 0x8,
- HasCatchOrWith = 0x10,
- CanUseSimpleCall = 0x20
+// Unused = 0x8,
+ HasCatchOrWith = 0x10
};
- // Absolute offset into file where the code for this function is located. Only used when the function
- // is serialized.
- quint64_le codeOffset;
- quint64_le codeSize;
+ // Absolute offset into file where the code for this function is located.
+ quint32_le codeOffset;
+ quint32_le codeSize;
quint32_le nameIndex;
quint32_le nFormals;
quint32_le formalsOffset;
quint32_le nLocals;
quint32_le localsOffset;
+ quint32_le nLineNumbers;
+ quint32_le lineNumberOffset;
quint32_le nInnerFunctions;
+ quint32_le nRegisters;
Location location;
// Qml Extensions Begin
@@ -249,6 +250,7 @@ struct Function
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+ const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset); }
const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); }
const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); }
const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); }
@@ -258,13 +260,23 @@ struct Function
const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
// ---
+ const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; }
+
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
- static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies) {
- return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7;
+ static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int codeSize) {
+ int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies +
+ 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
+ size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 72, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 76, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Qml data structures
@@ -860,40 +872,59 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData;
struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
{
- QV4::Heap::String **runtimeStrings = 0; // Array
+ // pointers either to data->constants() or little-endian memory copy.
+ QV4::Heap::String **runtimeStrings = nullptr; // Array
+ const Value* constants = nullptr;
+ QV4::Value *runtimeRegularExpressions = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *));
-struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount
+struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
{
+public:
+ CompilationUnit();
#ifdef V4_BOOTSTRAP
- CompilationUnit()
- : data(0)
- {}
- virtual ~CompilationUnit() {}
+ ~CompilationUnit() {}
#else
- CompilationUnit();
- virtual ~CompilationUnit();
+ ~CompilationUnit();
#endif
- const Unit *data;
+ void addref()
+ {
+ Q_ASSERT(refCount.load() > 0);
+ refCount.ref();
+ }
+
+ void release()
+ {
+ Q_ASSERT(refCount.load() > 0);
+ if (!refCount.deref())
+ destroy();
+ }
+ int count() const
+ {
+ return refCount.load();
+ }
+
+ const Unit *data = nullptr;
// Called only when building QML, when we build the header for JS first and append QML data
- virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
+ QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
#ifndef V4_BOOTSTRAP
QIntrusiveListNode nextCompilationUnit;
- ExecutionEngine *engine;
- QQmlEnginePrivate *qmlEngine; // only used in QML environment for composite types, not in plain QJSEngine case.
+ ExecutionEngine *engine = nullptr;
+ QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
QString fileName() const { return data->stringAt(data->sourceFileIndex); }
QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
- QV4::Lookup *runtimeLookups;
- QV4::Value *runtimeRegularExpressions;
- QV4::InternalClass **runtimeClasses;
+ QV4::Lookup *runtimeLookups = nullptr;
+ QV4::InternalClass **runtimeClasses = nullptr;
QVector<QV4::Function *> runtimeFunctions;
mutable QQmlNullableValue<QUrl> m_url;
@@ -913,23 +944,20 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public
QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache;
IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex);
- // pointers either to data->constants() or little-endian memory copy.
- const Value* constants;
-
void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
- int totalBindingsCount; // Number of bindings used in this type
- int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses
- int totalObjectCount; // Number of objects explicitly instantiated
+ int totalBindingsCount = 0; // Number of bindings used in this type
+ int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int totalObjectCount = 0; // Number of objects explicitly instantiated
QVector<QQmlScriptData *> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
- int metaTypeId;
- int listMetaTypeId;
- bool isRegisteredWithEngine;
+ int metaTypeId = -1;
+ int listMetaTypeId = -1;
+ bool isRegisteredWithEngine = false;
QScopedPointer<CompilationUnitMapper> backingFile;
@@ -960,25 +988,23 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public
void markObjects(MarkStack *markStack);
- void destroy() override;
-
- bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString);
+ bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
protected:
- virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
- virtual bool memoryMapCode(QString *errorString);
+ void linkBackendToEngine(QV4::ExecutionEngine *engine);
#endif // V4_BOOTSTRAP
+private:
+ void destroy();
+
+ QAtomicInt refCount = 1;
+
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &outputFileName, QString *errorString);
#else
bool saveToDisk(const QUrl &unitUrl, QString *errorString);
#endif
-
-protected:
- virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit);
- virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString);
};
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index f02ee728c9..96c9307513 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -39,10 +39,12 @@
#include <qv4compiler_p.h>
#include <qv4compileddata_p.h>
-#include <qv4isel_p.h>
+#include <qv4codegen_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsast_p.h>
#include <wtf/MathExtras.h>
#include <QCryptographicHash>
@@ -98,70 +100,66 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
}
}
-QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module)
- : irModule(module)
+QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module)
+ : module(module)
{
// Make sure the empty string always gets index 0
registerString(QString());
}
-uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup()
+int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_IndexedGetter;
- l.nameIndex = 0;
- lookups << l;
- return lookups.size() - 1;
+ return registerGetterLookup(registerString(name));
}
-uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup()
+int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter;
- l.nameIndex = 0;
+ l.type_and_flags = CompiledData::Lookup::Type_Getter;
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Getter;
- l.nameIndex = registerString(name);
- lookups << l;
- return lookups.size() - 1;
+ return registerSetterLookup(registerString(name));
}
-
-uint QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
{
CompiledData::Lookup l;
l.type_and_flags = CompiledData::Lookup::Type_Setter;
- l.nameIndex = registerString(name);
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-uint QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+{
+ return registerGlobalGetterLookup(registerString(name));
+}
+
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
- l.nameIndex = registerString(name);
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerRegExp(QV4::IR::RegExp *regexp)
+int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
CompiledData::RegExp re;
- re.stringIndex = registerString(*regexp->value);
+ re.stringIndex = registerString(regexp->pattern.toString());
re.flags = 0;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Global)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Global)
re.flags |= CompiledData::RegExp::RegExp_Global;
- if (regexp->flags & QV4::IR::RegExp::RegExp_IgnoreCase)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Multiline)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
re.flags |= CompiledData::RegExp::RegExp_Multiline;
regexps.append(re);
@@ -177,50 +175,62 @@ int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v)
return constants.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *args)
+QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx)
+{
+ return constants.at(idx);
+}
+
+int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members)
{
// ### re-use existing class definitions.
- const int size = CompiledData::JSClass::calculateSize(count);
+ const int size = CompiledData::JSClass::calculateSize(members.size());
jsClassOffsets.append(jsClassData.size());
const int oldSize = jsClassData.size();
jsClassData.resize(jsClassData.size() + size);
memset(jsClassData.data() + oldSize, 0, size);
CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
- jsClass->nMembers = count;
+ jsClass->nMembers = members.size();
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- IR::ExprList *it = args;
- for (int i = 0; i < count; ++i, it = it->next, ++member) {
- QV4::IR::Name *name = it->expr->asName();
- it = it->next;
+ for (const MemberInfo &memberInfo : members) {
+ member->nameOffset = registerString(memberInfo.name);
+ member->isAccessor = memberInfo.isAccessor;
+ ++member;
+ }
- const bool isData = it->expr->asConst()->value;
- it = it->next;
+ return jsClassOffsets.size() - 1;
+}
- member->nameOffset = registerString(*name->id);
- member->isAccessor = !isData;
+int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members)
+{
+ const int size = CompiledData::JSClass::calculateSize(count);
+ jsClassOffsets.append(jsClassData.size());
+ const int oldSize = jsClassData.size();
+ jsClassData.resize(jsClassData.size() + size);
+ memset(jsClassData.data() + oldSize, 0, size);
- if (!isData)
- it = it->next;
- }
+ CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
+ jsClass->nMembers = count;
+ CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
+ memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count);
return jsClassOffsets.size() - 1;
}
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
{
- registerString(irModule->fileName);
- for (QV4::IR::Function *f : qAsConst(irModule->functions)) {
- registerString(*f->name);
- for (int i = 0; i < f->formals.size(); ++i)
- registerString(*f->formals.at(i));
+ registerString(module->fileName);
+ for (Context *f : qAsConst(module->functions)) {
+ registerString(f->name);
+ for (int i = 0; i < f->arguments.size(); ++i)
+ registerString(f->arguments.at(i));
for (int i = 0; i < f->locals.size(); ++i)
- registerString(*f->locals.at(i));
+ registerString(f->locals.at(i));
}
- Q_ALLOCA_VAR(quint32_le, functionOffsets, irModule->functions.size() * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
@@ -235,9 +245,9 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le));
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *function = irModule->functions.at(i);
- if (function == irModule->rootFunction)
+ for (int i = 0; i < module->functions.size(); ++i) {
+ Context *function = module->functions.at(i);
+ if (function == module->rootContext)
unit->indexOfRootFunction = i;
writeFunction(dataPtr + functionOffsets[i], function);
@@ -277,14 +287,14 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
return unit;
}
-void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const
+void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
quint32 currentOffset = sizeof(QV4::CompiledData::Function);
currentOffset = (currentOffset + 7) & ~quint32(0x7);
- function->nameIndex = getStringId(*irFunction->name);
+ function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
if (irFunction->hasDirectEval)
function->flags |= CompiledData::Function::HasDirectEval;
@@ -292,13 +302,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
function->flags |= CompiledData::Function::UsesArgumentsObject;
if (irFunction->isStrict)
function->flags |= CompiledData::Function::IsStrict;
- if (irFunction->isNamedExpression)
- function->flags |= CompiledData::Function::IsNamedExpression;
if (irFunction->hasTry || irFunction->hasWith)
function->flags |= CompiledData::Function::HasCatchOrWith;
- if (irFunction->canUseSimpleCall())
- function->flags |= CompiledData::Function::CanUseSimpleCall;
- function->nFormals = irFunction->formals.size();
+ function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
@@ -306,7 +312,13 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
function->localsOffset = currentOffset;
currentOffset += function->nLocals * sizeof(quint32);
- function->nInnerFunctions = irFunction->nestedFunctions.size();
+ function->nLineNumbers = irFunction->lineNumberMapping.size();
+ function->lineNumberOffset = currentOffset;
+ currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
+
+ function->nInnerFunctions = irFunction->nestedContexts.size();
+
+ function->nRegisters = irFunction->registerCount;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -333,18 +345,21 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
function->location.line = irFunction->line;
function->location.column = irFunction->column;
- function->codeOffset = 0;
- function->codeSize = 0;
+ function->codeOffset = currentOffset;
+ function->codeSize = irFunction->code.size();
// write formals
quint32_le *formals = (quint32_le *)(f + function->formalsOffset);
- for (int i = 0; i < irFunction->formals.size(); ++i)
- formals[i] = getStringId(*irFunction->formals.at(i));
+ for (int i = 0; i < irFunction->arguments.size(); ++i)
+ formals[i] = getStringId(irFunction->arguments.at(i));
// write locals
quint32_le *locals = (quint32_le *)(f + function->localsOffset);
for (int i = 0; i < irFunction->locals.size(); ++i)
- locals[i] = getStringId(*irFunction->locals.at(i));
+ locals[i] = getStringId(irFunction->locals.at(i));
+
+ // write line numbers
+ memcpy(f + function->lineNumberOffset, irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
// write QML dependencies
quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset);
@@ -364,6 +379,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
+
+ // write byte code
+ memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
}
QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset)
@@ -372,17 +390,17 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
memset(&unit, 0, sizeof(unit));
memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
unit.flags = QV4::CompiledData::Unit::IsJavascript;
- unit.flags |= irModule->unitFlags;
+ unit.flags |= module->unitFlags;
unit.version = QV4_DATA_STRUCTURE_VERSION;
unit.qtVersion = QT_VERSION;
memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
- unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI);
+ unit.architectureIndex = registerString(module->targetABI.isEmpty() ? QSysInfo::buildAbi() : module->targetABI);
unit.codeGeneratorIndex = registerString(codeGeneratorName);
memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
quint32 nextOffset = sizeof(CompiledData::Unit);
- unit.functionTableSize = irModule->functions.size();
+ unit.functionTableSize = module->functions.size();
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
@@ -410,13 +428,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
nextOffset = (nextOffset + 7) & ~quint32(0x7);
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *f = irModule->functions.at(i);
+ for (int i = 0; i < module->functions.size(); ++i) {
+ Context *f = module->functions.at(i);
functionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
- nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount);
+ nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
+ qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
}
if (option == GenerateWithStringTable) {
@@ -428,8 +447,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToStringTable = 0;
}
unit.indexOfRootFunction = -1;
- unit.sourceFileIndex = getStringId(irModule->fileName);
- unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0;
+ unit.sourceFileIndex = getStringId(module->fileName);
+ unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0;
unit.nImports = 0;
unit.offsetToImports = 0;
unit.nObjects = 0;
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 9c51a44bad..360af6540f 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -51,8 +51,11 @@
//
#include <QtCore/qstring.h>
-#include "qv4jsir_p.h"
-#include <private/qendian_p.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
+#include <private/qv4global_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qv4compileddata_p.h>
QT_BEGIN_NAMESPACE
@@ -90,23 +93,31 @@ private:
};
struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
- JSUnitGenerator(IR::Module *module);
+ struct MemberInfo {
+ QString name;
+ bool isAccessor;
+ };
+
+ JSUnitGenerator(Module *module);
int registerString(const QString &str) { return stringTable.registerString(str); }
int getStringId(const QString &string) const { return stringTable.getStringId(string); }
QString stringForIndex(int index) const { return stringTable.stringForIndex(index); }
- uint registerGetterLookup(const QString &name);
- uint registerSetterLookup(const QString &name);
- uint registerGlobalGetterLookup(const QString &name);
- uint registerIndexedGetterLookup();
- uint registerIndexedSetterLookup();
+ int registerGetterLookup(const QString &name);
+ int registerGetterLookup(int nameIndex);
+ int registerSetterLookup(const QString &name);
+ int registerSetterLookup(int nameIndex);
+ int registerGlobalGetterLookup(const QString &name);
+ int registerGlobalGetterLookup(int nameIndex);
- int registerRegExp(IR::RegExp *regexp);
+ int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
int registerConstant(ReturnedValue v);
+ ReturnedValue constant(int idx);
- int registerJSClass(int count, IR::ExprList *args);
+ int registerJSClass(const QVector<MemberInfo> &members);
+ int registerJSClass(int count, CompiledData::JSClassMember *members);
enum GeneratorOption {
GenerateWithStringTable,
@@ -115,14 +126,14 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
// Returns bytes written
- void writeFunction(char *f, IR::Function *irFunction) const;
+ void writeFunction(char *f, Context *irFunction) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
private:
CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset);
- IR::Module *irModule;
+ Module *module;
QList<CompiledData::Lookup> lookups;
QVector<CompiledData::RegExp> regexps;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
new file mode 100644
index 0000000000..3293dc53c8
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4compilercontext_p.h"
+#include "qv4compilercontrolflow_p.h"
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
+
+QT_BEGIN_NAMESPACE
+
+Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
+{
+ Context *c = new Context(parent, compilationMode);
+ if (node) {
+ SourceLocation loc = node->firstSourceLocation();
+ c->line = loc.startLine;
+ c->column = loc.startColumn;
+ }
+
+ contextMap.insert(node, c);
+
+ if (!parent)
+ rootContext = c;
+ else {
+ parent->nestedContexts.append(c);
+ c->isStrict = parent->isStrict;
+ }
+
+ return c;
+}
+
+bool Context::forceLookupByName()
+{
+ ControlFlow *flow = controlFlow;
+ while (flow) {
+ if (flow->needsLookupByName)
+ return true;
+ flow = flow->parent;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
new file mode 100644
index 0000000000..3db30ea23d
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTEXT_P_H
+#define QV4COMPILERCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qv4global_p.h"
+#include <private/qqmljsast_p.h>
+#include <private/qv4compileddata_p.h>
+#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
+#include <QtCore/QStack>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow;
+
+enum CompilationMode {
+ GlobalCode,
+ EvalCode,
+ FunctionCode,
+ QmlBinding // This is almost the same as EvalCode, except:
+ // * function declarations are moved to the return address when encountered
+ // * return statements are allowed everywhere (like in FunctionCode)
+ // * variable declarations are treated as true locals (like in FunctionCode)
+};
+
+struct Context;
+
+struct Module {
+ Module(bool debugMode)
+ : debugMode(debugMode)
+ {}
+ ~Module() {
+ qDeleteAll(contextMap);
+ }
+
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode);
+
+ QHash<QQmlJS::AST::Node *, Context *> contextMap;
+ QList<Context *> functions;
+ Context *rootContext;
+ QString fileName;
+ QDateTime sourceTimeStamp;
+ uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
+ bool debugMode = false;
+ QString targetABI; // ### seems unused currently
+};
+
+
+struct Context {
+ Context *parent;
+ QString name;
+ int line = 0;
+ int column = 0;
+ int registerCount = 0;
+ int functionIndex = -1;
+
+ enum MemberType {
+ UndefinedMember,
+ ThisFunctionName,
+ VariableDefinition,
+ VariableDeclaration,
+ FunctionDefinition
+ };
+
+ struct Member {
+ MemberType type = UndefinedMember;
+ int index = -1;
+ QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope;
+ mutable bool canEscape = false;
+ QQmlJS::AST::FunctionExpression *function = 0;
+
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; }
+ };
+ typedef QMap<QString, Member> MemberMap;
+
+ MemberMap members;
+ QSet<QString> usedVariables;
+ QQmlJS::AST::FormalParameterList *formals = 0;
+ QStringList arguments;
+ QStringList locals;
+ QVector<Context *> nestedContexts;
+
+ ControlFlow *controlFlow = 0;
+ QByteArray code;
+ QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
+
+ int maxNumberOfArguments = 0;
+ bool hasDirectEval = false;
+ bool hasNestedFunctions = false;
+ bool isStrict = false;
+ bool usesThis = false;
+ bool hasTry = false;
+ bool hasWith = false;
+ mutable bool argumentsCanEscape = false;
+
+ enum UsesArgumentsObject {
+ ArgumentsObjectUnknown,
+ ArgumentsObjectNotUsed,
+ ArgumentsObjectUsed
+ };
+
+ UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
+
+ CompilationMode compilationMode;
+
+ template <typename T>
+ class SmallSet: public QVarLengthArray<T, 8>
+ {
+ public:
+ void insert(int value)
+ {
+ for (auto it : *this) {
+ if (it == value)
+ return;
+ }
+ this->append(value);
+ }
+ };
+
+ // Map from meta property index (existence implies dependency) to notify signal index
+ struct KeyValuePair
+ {
+ quint32 _key;
+ quint32 _value;
+
+ KeyValuePair(): _key(0), _value(0) {}
+ KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
+
+ quint32 key() const { return _key; }
+ quint32 value() const { return _value; }
+ };
+
+ class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
+ {
+ public:
+ void insert(quint32 key, quint32 value)
+ {
+ for (auto it = begin(), eit = end(); it != eit; ++it) {
+ if (it->_key == key) {
+ it->_value = value;
+ return;
+ }
+ }
+ append(KeyValuePair(key, value));
+ }
+ };
+
+ // Qml extension:
+ SmallSet<int> idObjectDependencies;
+ PropertyDependencyMap contextObjectPropertyDependencies;
+ PropertyDependencyMap scopeObjectPropertyDependencies;
+
+ Context(Context *parent, CompilationMode mode)
+ : parent(parent)
+ , compilationMode(mode)
+ {
+ if (parent && parent->isStrict)
+ isStrict = true;
+ }
+
+ bool forceLookupByName();
+
+
+ bool canUseSimpleCall() const {
+ return nestedContexts.isEmpty() &&
+ locals.isEmpty() &&
+ !hasTry && !hasWith &&
+ (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval;
+ }
+
+ int findArgument(const QString &name)
+ {
+ // search backwards to handle duplicate argument names correctly
+ for (int i = arguments.size() - 1; i >= 0; --i) {
+ if (arguments.at(i) == name)
+ return i;
+ }
+ return -1;
+ }
+
+ Member findMember(const QString &name) const
+ {
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end())
+ return Member();
+ Q_ASSERT(it->index != -1 || !parent);
+ return (*it);
+ }
+
+ bool memberInfo(const QString &name, const Member **m) const
+ {
+ Q_ASSERT(m);
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end()) {
+ *m = 0;
+ return false;
+ }
+ *m = &(*it);
+ return true;
+ }
+
+ void addUsedVariable(const QString &name) {
+ usedVariables.insert(name);
+ }
+
+ void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = 0)
+ {
+ if (! name.isEmpty()) {
+ if (type != FunctionDefinition) {
+ for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
+ if (it->name == name)
+ return;
+ }
+ MemberMap::iterator it = members.find(name);
+ if (it == members.end()) {
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ members.insert(name, m);
+ } else {
+ Q_ASSERT(scope == (*it).scope);
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
+ }
+ }
+ }
+};
+
+
+} } // namespace QV4::Compiler
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
new file mode 100644
index 0000000000..13716fe29f
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTROLFLOW_P_H
+#define QV4COMPILERCONTROLFLOW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qqmljsast_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow {
+ using Reference = Codegen::Reference;
+ using BytecodeGenerator = Moth::BytecodeGenerator;
+ using Instruction = Moth::Instruction;
+
+ enum Type {
+ Loop,
+ With,
+ Finally,
+ Catch
+ };
+
+ enum HandlerType {
+ Invalid,
+ Break,
+ Continue,
+ Return,
+ Throw
+ };
+
+ struct Handler {
+ HandlerType type;
+ QString label;
+ BytecodeGenerator::Label linkLabel;
+ int tempIndex;
+ int value;
+ };
+
+ Codegen *cg;
+ ControlFlow *parent;
+ Type type;
+ bool needsLookupByName = false;
+
+ ControlFlow(Codegen *cg, Type type)
+ : cg(cg), parent(cg->_context->controlFlow), type(type)
+ {
+ cg->_context->controlFlow = this;
+ }
+
+ virtual ~ControlFlow() {
+ cg->_context->controlFlow = parent;
+ }
+
+ void emitReturnStatement() const {
+ if (cg->_returnAddress >= 0) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
+ generator()->addInstruction(load);
+ }
+ Instruction::Ret ret;
+ cg->bytecodeGenerator->addInstruction(ret);
+ }
+
+ void jumpToHandler(const Handler &h) {
+ if (h.linkLabel.isReturn()) {
+ emitReturnStatement();
+ } else {
+ if (h.tempIndex >= 0)
+ Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
+ cg->bytecodeGenerator->jump().link(h.linkLabel);
+ }
+ }
+
+ bool returnRequiresUnwind() const {
+ const ControlFlow *f = this;
+ while (f) {
+ if (f->type == Finally)
+ return true;
+ f = f->parent;
+ }
+ return false;
+ }
+
+ virtual QString label() const { return QString(); }
+
+ bool isSimple() const {
+ return type == Loop;
+ }
+
+ Handler getParentHandler(HandlerType type, const QString &label = QString()) {
+ if (parent)
+ return parent->getHandler(type, label);
+ switch (type) {
+ case Break:
+ case Continue:
+ return { Invalid, QString(), {}, -1, 0 };
+ case Return:
+ case Throw:
+ return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
+ case Invalid:
+ break;
+ }
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
+
+ BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
+ return parent ? parent->exceptionHandler() : 0;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return parentExceptionHandler();
+ }
+
+
+ virtual void handleThrow(const Reference &expr) {
+ Reference e = expr;
+ Handler h = getHandler(ControlFlow::Throw);
+ if (h.tempIndex >= 0) {
+ e = e.storeOnStack();
+ Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
+ }
+ e.loadInAccumulator();
+ Instruction::ThrowException instr;
+ generator()->addInstruction(instr);
+ }
+
+protected:
+ QString loopLabel() const {
+ QString label;
+ if (cg->_labelledStatement) {
+ label = cg->_labelledStatement->label.toString();
+ cg->_labelledStatement = 0;
+ }
+ return label;
+ }
+ BytecodeGenerator *generator() const {
+ return cg->bytecodeGenerator;
+ }
+};
+
+struct ControlFlowLoop : public ControlFlow
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = 0;
+ BytecodeGenerator::Label *continueLabel = 0;
+
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = 0)
+ : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ {
+ }
+
+ virtual QString label() const { return loopLabel; }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return { type, loopLabel, *breakLabel, -1, 0 };
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return { type, loopLabel, *continueLabel, -1, 0 };
+ break;
+ case Return:
+ case Throw:
+ break;
+ case Invalid:
+ Q_ASSERT(false);
+ Q_UNREACHABLE();
+ }
+ return getParentHandler(type, label);
+ }
+
+};
+
+struct ControlFlowUnwind : public ControlFlow
+{
+ BytecodeGenerator::ExceptionHandler unwindLabel;
+ int controlFlowTemp;
+ QVector<Handler> handlers;
+
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ {
+ Q_ASSERT(type != Loop);
+ controlFlowTemp = static_cast<int>(generator()->newRegister());
+ Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
+ // we'll need at least a handler for throw
+ getHandler(Throw);
+ }
+
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(!isSimple());
+
+ Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
+ for (const auto &h : qAsConst(handlers)) {
+ Handler parentHandler = getParentHandler(h.type, h.label);
+
+ if (h.type == Throw || parentHandler.tempIndex >= 0) {
+ BytecodeGenerator::Label skip = generator()->newLabel();
+ generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
+ if (h.type == Throw)
+ emitForThrowHandling();
+ jumpToHandler(parentHandler);
+ skip.link();
+ } else {
+ if (parentHandler.linkLabel.isReturn()) {
+ BytecodeGenerator::Label skip = generator()->newLabel();
+ generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
+ emitReturnStatement();
+ skip.link();
+ } else {
+ generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
+ }
+ }
+ }
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ for (const auto &h : qAsConst(handlers)) {
+ if (h.type == type && h.label == label)
+ return h;
+ }
+ Handler h = {
+ type,
+ label,
+ unwindLabel,
+ controlFlowTemp,
+ handlers.size()
+ };
+ handlers.append(h);
+ return h;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return &unwindLabel;
+ }
+
+ virtual void emitForThrowHandling() { }
+};
+
+struct ControlFlowWith : public ControlFlowUnwind
+{
+ ControlFlowWith(Codegen *cg)
+ : ControlFlowUnwind(cg, With)
+ {
+ needsLookupByName = true;
+
+ savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister());
+
+ // assumes the with object is in the accumulator
+ Instruction::PushWithContext pushScope;
+ pushScope.reg = savedContextRegister;
+ generator()->addInstruction(pushScope);
+ generator()->setExceptionHandler(&unwindLabel);
+ }
+
+ virtual ~ControlFlowWith() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ generator()->setExceptionHandler(parentExceptionHandler());
+ Instruction::PopContext pop;
+ pop.reg = savedContextRegister;
+ generator()->addInstruction(pop);
+
+ emitUnwindHandler();
+ }
+ Moth::StackSlot savedContextRegister;
+};
+
+struct ControlFlowCatch : public ControlFlowUnwind
+{
+ AST::Catch *catchExpression;
+ bool insideCatch = false;
+ BytecodeGenerator::ExceptionHandler exceptionLabel;
+ BytecodeGenerator::ExceptionHandler catchUnwindLabel;
+
+ ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
+ : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
+ exceptionLabel(generator()->newExceptionHandler()),
+ catchUnwindLabel(generator()->newExceptionHandler())
+ {
+ generator()->setExceptionHandler(&exceptionLabel);
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ Handler h = getParentHandler(type, label);
+ if (h.type == Invalid)
+ return h;
+ h = ControlFlowUnwind::getHandler(type, label);
+ if (insideCatch)
+ // if we're inside the catch block, we need to jump to the pop scope
+ // instruction at the end of the catch block, not the unwind handler
+ h.linkLabel = catchUnwindLabel;
+ else if (type == Throw)
+ // if we're inside the try block, we need to jump to the catch block,
+ // not the unwind handler
+ h.linkLabel = exceptionLabel;
+ return h;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ }
+
+ ~ControlFlowCatch() {
+ // emit code for unwinding
+
+ needsLookupByName = true;
+ insideCatch = true;
+
+ Codegen::RegisterScope scope(cg);
+
+ // exceptions inside the try block go here
+ exceptionLabel.link();
+ Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister());
+ Instruction::PushCatchContext pushCatch;
+ pushCatch.name = cg->registerString(catchExpression->name.toString());
+ pushCatch.reg = savedContextReg;
+ generator()->addInstruction(pushCatch);
+ // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
+ Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
+ generator()->setExceptionHandler(&catchUnwindLabel);
+
+ cg->statement(catchExpression->statement);
+
+ insideCatch = false;
+ needsLookupByName = false;
+
+ // exceptions inside catch and break/return statements go here
+ catchUnwindLabel.link();
+ Instruction::PopContext pop;
+ pop.reg = savedContextReg;
+ generator()->addInstruction(pop);
+
+ // break/continue/return statements in try go here
+ unwindLabel.link();
+ generator()->setExceptionHandler(parentExceptionHandler());
+
+ emitUnwindHandler();
+ }
+};
+
+struct ControlFlowFinally : public ControlFlowUnwind
+{
+ AST::Finally *finally;
+ bool insideFinally = false;
+ int exceptionTemp = -1;
+
+ ControlFlowFinally(Codegen *cg, AST::Finally *finally)
+ : ControlFlowUnwind(cg, Finally), finally(finally)
+ {
+ Q_ASSERT(finally != 0);
+ generator()->setExceptionHandler(&unwindLabel);
+ }
+
+ virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
+ // if we're inside the finally block, any exceptions etc. should
+ // go directly to the parent handler
+ if (insideFinally)
+ return getParentHandler(type, label);
+ return ControlFlowUnwind::getHandler(type, label);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
+ return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ }
+
+ ~ControlFlowFinally() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ Codegen::RegisterScope scope(cg);
+
+ Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
+ Instruction::StoreReg storeRetVal;
+ storeRetVal.reg = retVal;
+ generator()->addInstruction(storeRetVal);
+
+ insideFinally = true;
+ exceptionTemp = generator()->newRegister();
+ Instruction::GetException instr;
+ generator()->addInstruction(instr);
+ Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
+
+ generator()->setExceptionHandler(parentExceptionHandler());
+ cg->statement(finally->statement);
+ insideFinally = false;
+
+ Instruction::LoadReg loadRetVal;
+ loadRetVal.reg = retVal;
+ generator()->addInstruction(loadRetVal);
+
+ emitUnwindHandler();
+ }
+
+ virtual void emitForThrowHandling() {
+ // reset the exception flag, that got cleared before executing the statements in finally
+ Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
+ Instruction::SetException setException;
+ Q_ASSERT(exceptionTemp != -1);
+ generator()->addInstruction(setException);
+ }
+};
+
+} } // QV4::Compiler namespace
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
new file mode 100644
index 0000000000..6a9b064bd8
--- /dev/null
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4compilerscanfunctions_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+#include <QtCore/QBuffer>
+#include <QtCore/QBitArray>
+#include <QtCore/QLinkedList>
+#include <QtCore/QStack>
+#include <private/qqmljsast_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qv4string_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
+
+ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
+ : _cg(cg)
+ , _sourceCode(sourceCode)
+ , _context(0)
+ , _allowFuncDecls(true)
+ , defaultProgramMode(defaultProgramMode)
+{
+}
+
+void ScanFunctions::operator()(Node *node)
+{
+ if (node)
+ node->accept(this);
+
+ calcEscapingVariables();
+}
+
+void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
+{
+ Context *e = _cg->_module->newContext(node, _context, compilationMode);
+ if (!e->isStrict)
+ e->isStrict = _cg->_strictMode;
+ _contextStack.append(e);
+ _context = e;
+}
+
+void ScanFunctions::leaveEnvironment()
+{
+ _contextStack.pop();
+ _context = _contextStack.isEmpty() ? 0 : _contextStack.top();
+}
+
+void ScanFunctions::checkDirectivePrologue(SourceElements *ast)
+{
+ for (SourceElements *it = ast; it; it = it->next) {
+ if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
+ if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
+ if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
+ // Use the source code, because the StringLiteral's
+ // value might have escape sequences in it, which is not
+ // allowed.
+ if (strLit->literalToken.length < 2)
+ continue;
+ QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
+ if (str == QLatin1String("use strict")) {
+ _context->isStrict = true;
+ } else {
+ // TODO: give a warning.
+ }
+ continue;
+ }
+ }
+ }
+
+ break;
+ }
+}
+
+void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
+{
+ if (_context->isStrict) {
+ if (name == QLatin1String("implements")
+ || name == QLatin1String("interface")
+ || name == QLatin1String("let")
+ || name == QLatin1String("package")
+ || name == QLatin1String("private")
+ || name == QLatin1String("protected")
+ || name == QLatin1String("public")
+ || name == QLatin1String("static")
+ || name == QLatin1String("yield")) {
+ _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word"));
+ }
+ }
+}
+void ScanFunctions::checkForArguments(AST::FormalParameterList *parameters)
+{
+ while (parameters) {
+ if (parameters->name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ parameters = parameters->next;
+ }
+}
+
+bool ScanFunctions::visit(Program *ast)
+{
+ enterEnvironment(ast, defaultProgramMode);
+ checkDirectivePrologue(ast->elements);
+ return true;
+}
+
+void ScanFunctions::endVisit(Program *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(CallExpression *ast)
+{
+ if (! _context->hasDirectEval) {
+ if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("eval")) {
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ _context->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ _context->hasDirectEval = true;
+ }
+ }
+ }
+ int argc = 0;
+ for (ArgumentList *it = ast->arguments; it; it = it->next)
+ ++argc;
+ _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+ return true;
+}
+
+bool ScanFunctions::visit(NewMemberExpression *ast)
+{
+ int argc = 0;
+ for (ArgumentList *it = ast->arguments; it; it = it->next)
+ ++argc;
+ _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+ return true;
+}
+
+bool ScanFunctions::visit(ArrayLiteral *ast)
+{
+ int index = 0;
+ for (ElementList *it = ast->elements; it; it = it->next) {
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ ++index;
+ ++index;
+ }
+ if (ast->elision) {
+ for (Elision *elision = ast->elision->next; elision; elision = elision->next)
+ ++index;
+ }
+ _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index);
+ return true;
+}
+
+bool ScanFunctions::visit(VariableDeclaration *ast)
+{
+ if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ checkName(ast->name, ast->identifierToken);
+ if (ast->name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
+ return false;
+ }
+ QString name = ast->name.toString();
+ const Context::Member *m = 0;
+ if (_context->memberInfo(name, &m)) {
+ if (m->isLexicallyScoped() || ast->isLexicallyScoped()) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
+ }
+ _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope);
+ return true;
+}
+
+bool ScanFunctions::visit(IdentifierExpression *ast)
+{
+ checkName(ast->name, ast->identifierToken);
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ _context->addUsedVariable(ast->name.toString());
+ return true;
+}
+
+bool ScanFunctions::visit(ExpressionStatement *ast)
+{
+ if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
+ if (!_allowFuncDecls)
+ _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
+
+ enterFunction(expr, /*enterName*/ true);
+ Node::accept(expr->formals, this);
+ Node::accept(expr->body, this);
+ leaveEnvironment();
+ return false;
+ } else {
+ SourceLocation firstToken = ast->firstSourceLocation();
+ if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) {
+ _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token"));
+ }
+ }
+ return true;
+}
+
+bool ScanFunctions::visit(FunctionExpression *ast)
+{
+ enterFunction(ast, /*enterName*/ false);
+ return true;
+}
+
+void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+{
+ if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
+ enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0);
+}
+
+void ScanFunctions::endVisit(FunctionExpression *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ObjectLiteral *ast)
+{
+ int argc = 0;
+ for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
+ QString key = it->assignment->name->asString();
+ if (QV4::String::toArrayIndex(key) != UINT_MAX)
+ ++argc;
+ ++argc;
+ if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
+ ++argc;
+ }
+ _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
+}
+
+bool ScanFunctions::visit(PropertyGetterSetter *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0);
+ return true;
+}
+
+void ScanFunctions::endVisit(PropertyGetterSetter *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(FunctionDeclaration *ast)
+{
+ enterFunction(ast, /*enterName*/ true);
+ return true;
+}
+
+void ScanFunctions::endVisit(FunctionDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(TryStatement *)
+{
+ // ### should limit to catch(), as try{} finally{} should be ok without
+ _context->hasTry = true;
+ return true;
+}
+
+bool ScanFunctions::visit(WithStatement *ast)
+{
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
+ return false;
+ }
+
+ _context->hasWith = true;
+ return true;
+}
+
+bool ScanFunctions::visit(DoWhileStatement *ast) {
+ {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+ }
+ Node::accept(ast->expression, this);
+ return false;
+}
+
+bool ScanFunctions::visit(ForStatement *ast) {
+ Node::accept(ast->initialiser, this);
+ Node::accept(ast->condition, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+bool ScanFunctions::visit(LocalForStatement *ast) {
+ Node::accept(ast->declarations, this);
+ Node::accept(ast->condition, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+bool ScanFunctions::visit(ForEachStatement *ast) {
+ Node::accept(ast->initialiser, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+bool ScanFunctions::visit(LocalForEachStatement *ast) {
+ Node::accept(ast->declaration, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+bool ScanFunctions::visit(ThisExpression *)
+{
+ _context->usesThis = true;
+ return false;
+}
+
+bool ScanFunctions::visit(Block *ast) {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ Node::accept(ast->statements, this);
+ return false;
+}
+
+void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
+{
+ if (_context) {
+ _context->hasNestedFunctions = true;
+ // The identifier of a function expression cannot be referenced from the enclosing environment.
+ if (expr)
+ _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr);
+ if (name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+
+ enterEnvironment(ast, FunctionCode);
+ checkForArguments(formals);
+
+ if (!name.isEmpty())
+ _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope);
+ _context->formals = formals;
+
+ if (body && !_context->isStrict)
+ checkDirectivePrologue(body->elements);
+
+ for (FormalParameterList *it = formals; it; it = it->next) {
+ QString arg = it->name.toString();
+ int duplicateIndex = _context->arguments.indexOf(arg);
+ if (duplicateIndex != -1) {
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
+ return;
+ } else {
+ // change the name of the earlier argument to enforce the specified lookup semantics
+ QString modified = arg;
+ while (_context->arguments.contains(modified))
+ modified += QString(0xfffe);
+ _context->arguments[duplicateIndex] = modified;
+ }
+ }
+ if (_context->isStrict) {
+ if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
+ _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
+ return;
+ }
+ }
+ _context->arguments += arg;
+ }
+}
+
+void ScanFunctions::calcEscapingVariables()
+{
+ Module *m = _cg->_module;
+
+ for (Context *inner : m->contextMap) {
+ for (const QString &var : qAsConst(inner->usedVariables)) {
+ Context *c = inner;
+ while (c) {
+ Context::MemberMap::const_iterator it = c->members.find(var);
+ if (it != c->members.end()) {
+ if (c != inner)
+ it->canEscape = true;
+ break;
+ }
+ if (c->findArgument(var) != -1) {
+ if (c != inner)
+ c->argumentsCanEscape = true;
+ break;
+ }
+ c = c->parent;
+ }
+ }
+ Context *c = inner->parent;
+ while (c) {
+ c->hasDirectEval |= inner->hasDirectEval;
+ c = c->parent;
+ }
+ }
+
+ static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS");
+ if (showEscapingVars) {
+ qDebug() << "==== escaping variables ====";
+ for (Context *c : m->contextMap) {
+ qDebug() << "Context" << c->name << ":";
+ qDebug() << " Arguments escape" << c->argumentsCanEscape;
+ for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
+ qDebug() << " " << it.key() << it.value().canEscape;
+ }
+ }
+ }
+}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
new file mode 100644
index 0000000000..0b898e587d
--- /dev/null
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERSCANFUNCTIONS_P_H
+#define QV4COMPILERSCANFUNCTIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qv4global_p.h"
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4util_p.h>
+#include <QtCore/QStringList>
+#include <QStack>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+
+namespace QV4 {
+
+namespace Moth {
+struct Instruction;
+}
+
+namespace CompiledData {
+struct CompilationUnit;
+}
+
+namespace Compiler {
+
+class Codegen;
+
+class ScanFunctions: protected QQmlJS::AST::Visitor
+{
+ typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
+public:
+ ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
+ void operator()(AST::Node *node);
+
+ void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
+ void leaveEnvironment();
+
+ void enterQmlScope(AST::Node *ast, const QString &name)
+ { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0); }
+
+ void enterQmlFunction(AST::FunctionDeclaration *ast)
+ { enterFunction(ast, false); }
+
+protected:
+ using Visitor::visit;
+ using Visitor::endVisit;
+
+ void checkDirectivePrologue(AST::SourceElements *ast);
+
+ void checkName(const QStringRef &name, const AST::SourceLocation &loc);
+ void checkForArguments(AST::FormalParameterList *parameters);
+
+ bool visit(AST::Program *ast) override;
+ void endVisit(AST::Program *) override;
+
+ bool visit(AST::CallExpression *ast) override;
+ bool visit(AST::NewMemberExpression *ast) override;
+ bool visit(AST::ArrayLiteral *ast) override;
+ bool visit(AST::VariableDeclaration *ast) override;
+ bool visit(AST::IdentifierExpression *ast) override;
+ bool visit(AST::ExpressionStatement *ast) override;
+ bool visit(AST::FunctionExpression *ast) override;
+
+ void enterFunction(AST::FunctionExpression *ast, bool enterName);
+
+ void endVisit(AST::FunctionExpression *) override;
+
+ bool visit(AST::ObjectLiteral *ast) override;
+
+ bool visit(AST::PropertyGetterSetter *ast) override;
+ void endVisit(AST::PropertyGetterSetter *) override;
+
+ bool visit(AST::FunctionDeclaration *ast) override;
+ void endVisit(AST::FunctionDeclaration *) override;
+
+ bool visit(AST::TryStatement *ast) override;
+ bool visit(AST::WithStatement *ast) override;
+
+ bool visit(AST::DoWhileStatement *ast) override;
+ bool visit(AST::ForStatement *ast) override;
+ bool visit(AST::LocalForStatement *ast) override;
+ bool visit(AST::ForEachStatement *ast) override;
+ bool visit(AST::LocalForEachStatement *ast) override;
+ bool visit(AST::ThisExpression *ast) override;
+
+ bool visit(AST::Block *ast) override;
+
+protected:
+ void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr);
+
+ void calcEscapingVariables();
+// fields:
+ Codegen *_cg;
+ const QString _sourceCode;
+ Context *_context;
+ QStack<Context *> _contextStack;
+
+ bool _allowFuncDecls;
+ CompilationMode defaultProgramMode;
+};
+
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index cf8cf623bc..9cfde99e6b 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -38,17 +38,603 @@
****************************************************************************/
#include "qv4instr_moth_p.h"
+#include <private/qv4compileddata_p.h>
using namespace QV4;
using namespace QV4::Moth;
-int Instr::size(Type type)
+int InstrInfo::size(Instr::Type type)
{
-#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size;
+#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size;
switch (type) {
FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
- default: return 0;
}
#undef MOTH_RETURN_INSTR_SIZE
+ Q_UNREACHABLE();
}
+static QByteArray alignedNumber(int n) {
+ QByteArray number = QByteArray::number(n);
+ while (number.size() < 8)
+ number.prepend(' ');
+ return number;
+}
+
+static QByteArray alignedLineNumber(int line) {
+ if (line > 0)
+ return alignedNumber(static_cast<int>(line));
+ return QByteArray(" ");
+}
+
+static QByteArray rawBytes(const char *data, int n)
+{
+ QByteArray ba;
+ while (n) {
+ uint num = *reinterpret_cast<const uchar *>(data);
+ if (num < 16)
+ ba += '0';
+ ba += QByteArray::number(num, 16) + " ";
+ ++data;
+ --n;
+ }
+ while (ba.size() < 25)
+ ba += ' ';
+ return ba;
+}
+
+static QString toString(QV4::ReturnedValue v)
+{
+#ifdef V4_BOOTSTRAP
+ return QStringLiteral("string-const(%1)").arg(v);
+#else // !V4_BOOTSTRAP
+ Value val = Value::fromReturnedValue(v);
+ QString result;
+ if (val.isInt32())
+ result = QLatin1String("int ");
+ else if (val.isDouble())
+ result = QLatin1String("double ");
+ if (val.isEmpty())
+ result += QLatin1String("empty");
+ else
+ result += val.toQStringNoThrow();
+ return result;
+#endif // V4_BOOTSTRAP
+}
+
+#define ABSOLUTE_OFFSET() \
+ (code - start + offset)
+
+#define MOTH_BEGIN_INSTR(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ QDebug d = qDebug(); \
+ d.noquote(); \
+ d.nospace(); \
+ d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
+ << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
+
+#define MOTH_END_INSTR(instr) \
+ continue; \
+ }
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace Moth {
+
+const int InstrInfo::argumentCount[] = {
+ FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS)
+};
+
+
+void dumpConstantTable(const Value *constants, uint count)
+{
+ QDebug d = qDebug();
+ d.nospace();
+ for (uint i = 0; i < count; ++i)
+ d << alignedNumber(int(i)).constData() << ": "
+ << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
+}
+
+QString dumpRegister(int reg, int nFormals)
+{
+ Q_STATIC_ASSERT(offsetof(CallData, function) == 0);
+ Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value));
+ Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value));
+ Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value));
+ if (reg == CallData::Function)
+ return QStringLiteral("(function)");
+ else if (reg == CallData::Context)
+ return QStringLiteral("(context)");
+ else if (reg == CallData::Accumulator)
+ return QStringLiteral("(accumulator)");
+ else if (reg == CallData::This)
+ return QStringLiteral("(this)");
+ else if (reg == CallData::Argc)
+ return QStringLiteral("(argc)");
+ reg -= 4;
+ if (reg <= nFormals)
+ return QStringLiteral("a%1").arg(reg);
+ reg -= nFormals;
+ return QStringLiteral("r%1").arg(reg);
+
+}
+
+QString dumpArguments(int argc, int argv, int nFormals)
+{
+ if (!argc)
+ return QStringLiteral("()");
+ return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")");
+}
+
+
+void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping)
+{
+ MOTH_JUMP_TABLE;
+
+ auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
+ return entry.codeOffset < offset;
+ };
+
+ int lastLine = -1;
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ const CompiledData::CodeOffsetToLine *codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast<uint>(code - start) + 1, findLine) - 1;
+ int line = int(codeToLine->line);
+ if (line != lastLine)
+ lastLine = line;
+ else
+ line = -1;
+
+ int codeOffset = int(code - start);
+
+ MOTH_DISPATCH()
+
+ MOTH_BEGIN_INSTR(LoadReg)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(LoadReg)
+
+ MOTH_BEGIN_INSTR(StoreReg)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(StoreReg)
+
+ MOTH_BEGIN_INSTR(MoveReg)
+ d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
+ MOTH_END_INSTR(MoveReg)
+
+ MOTH_BEGIN_INSTR(LoadConst)
+ d << "C" << index;
+ MOTH_END_INSTR(LoadConst)
+
+ MOTH_BEGIN_INSTR(LoadNull)
+ MOTH_END_INSTR(LoadNull)
+
+ MOTH_BEGIN_INSTR(LoadZero)
+ MOTH_END_INSTR(LoadZero)
+
+ MOTH_BEGIN_INSTR(LoadTrue)
+ MOTH_END_INSTR(LoadTrue)
+
+ MOTH_BEGIN_INSTR(LoadFalse)
+ MOTH_END_INSTR(LoadFalse)
+
+ MOTH_BEGIN_INSTR(LoadUndefined)
+ MOTH_END_INSTR(LoadUndefined)
+
+ MOTH_BEGIN_INSTR(LoadInt)
+ d << value;
+ MOTH_END_INSTR(LoadInt)
+
+ MOTH_BEGIN_INSTR(MoveConst)
+ d << dumpRegister(destTemp, nFormals) << ", C" << constIndex;
+ MOTH_END_INSTR(MoveConst)
+
+ MOTH_BEGIN_INSTR(LoadLocal)
+ if (index < nLocals)
+ d << "l" << index;
+ else
+ d << "a" << (index - nLocals);
+ MOTH_END_INSTR(LoadLocal)
+
+ MOTH_BEGIN_INSTR(StoreLocal)
+ if (index < nLocals)
+ d << "l" << index;
+ else
+ d << "a" << (index - nLocals);
+ MOTH_END_INSTR(StoreLocal)
+
+ MOTH_BEGIN_INSTR(LoadScopedLocal)
+ if (index < nLocals)
+ d << "l" << index << "@" << scope;
+ else
+ d << "a" << (index - nLocals) << "@" << scope;
+ MOTH_END_INSTR(LoadScopedLocal)
+
+ MOTH_BEGIN_INSTR(StoreScopedLocal)
+ if (index < nLocals)
+ d << ", " << "l" << index << "@" << scope;
+ else
+ d << ", " << "a" << (index - nLocals) << "@" << scope;
+ MOTH_END_INSTR(StoreScopedLocal)
+
+ MOTH_BEGIN_INSTR(LoadRuntimeString)
+ d << stringId;
+ MOTH_END_INSTR(LoadRuntimeString)
+
+ MOTH_BEGIN_INSTR(LoadRegExp)
+ d << regExpId;
+ MOTH_END_INSTR(LoadRegExp)
+
+ MOTH_BEGIN_INSTR(LoadClosure)
+ d << value;
+ MOTH_END_INSTR(LoadClosure)
+
+ MOTH_BEGIN_INSTR(LoadName)
+ d << name;
+ MOTH_END_INSTR(LoadName)
+
+ MOTH_BEGIN_INSTR(LoadGlobalLookup)
+ d << index;
+ MOTH_END_INSTR(LoadGlobalLookup)
+
+ MOTH_BEGIN_INSTR(StoreNameSloppy)
+ d << name;
+ MOTH_END_INSTR(StoreNameSloppy)
+
+ MOTH_BEGIN_INSTR(StoreNameStrict)
+ d << name;
+ MOTH_END_INSTR(StoreNameStrict)
+
+ MOTH_BEGIN_INSTR(LoadElement)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(LoadElementA)
+ d << dumpRegister(base, nFormals) << "[acc]";
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(StoreElement)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ MOTH_END_INSTR(StoreElement)
+
+ MOTH_BEGIN_INSTR(LoadProperty)
+ d << dumpRegister(base, nFormals) << "[" << name << "]";
+ MOTH_END_INSTR(LoadProperty)
+
+ MOTH_BEGIN_INSTR(LoadPropertyA)
+ d << "acc[" << name << "]";
+ MOTH_END_INSTR(LoadElementA)
+
+ MOTH_BEGIN_INSTR(GetLookup)
+ d << dumpRegister(base, nFormals) << "(" << index << ")";
+ MOTH_END_INSTR(GetLookup)
+
+ MOTH_BEGIN_INSTR(GetLookupA)
+ d << "acc(" << index << ")";
+ MOTH_END_INSTR(GetLookupA)
+
+ MOTH_BEGIN_INSTR(StoreProperty)
+ d << dumpRegister(base, nFormals) << "[" << name<< "]";
+ MOTH_END_INSTR(StoreProperty)
+
+ MOTH_BEGIN_INSTR(SetLookup)
+ d << dumpRegister(base, nFormals) << "(" << index << ")";
+ MOTH_END_INSTR(SetLookup)
+
+ MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
+ MOTH_END_INSTR(StoreScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
+ MOTH_END_INSTR(LoadScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(StoreContextObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
+ MOTH_END_INSTR(StoreContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadContextObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
+ MOTH_END_INSTR(LoadContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadIdObject)
+ d << dumpRegister(base, nFormals) << "[" << index << "]";
+ MOTH_END_INSTR(LoadIdObject)
+
+ MOTH_BEGIN_INSTR(CallValue)
+ d << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallValue)
+
+ MOTH_BEGIN_INSTR(CallProperty)
+ d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallProperty)
+
+ MOTH_BEGIN_INSTR(CallPropertyLookup)
+ d << dumpRegister(base, nFormals) << "." << lookupIndex << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallPropertyLookup)
+
+ MOTH_BEGIN_INSTR(CallElement)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallElement)
+
+ MOTH_BEGIN_INSTR(CallName)
+ d << name << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallName)
+
+ MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
+ d << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallPossiblyDirectEval)
+
+ MOTH_BEGIN_INSTR(CallGlobalLookup)
+ d << index << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallGlobalLookup)
+
+ MOTH_BEGIN_INSTR(SetExceptionHandler)
+ if (offset)
+ d << ABSOLUTE_OFFSET();
+ else
+ d << "<null>";
+ MOTH_END_INSTR(SetExceptionHandler)
+
+ MOTH_BEGIN_INSTR(ThrowException)
+ MOTH_END_INSTR(ThrowException)
+
+ MOTH_BEGIN_INSTR(GetException)
+ MOTH_END_INSTR(HasException)
+
+ MOTH_BEGIN_INSTR(SetException)
+ MOTH_END_INSTR(SetExceptionFlag)
+
+ MOTH_BEGIN_INSTR(CreateCallContext)
+ MOTH_END_INSTR(CreateCallContext)
+
+ MOTH_BEGIN_INSTR(PushCatchContext)
+ d << dumpRegister(reg, nFormals) << ", " << name;
+ MOTH_END_INSTR(PushCatchContext)
+
+ MOTH_BEGIN_INSTR(PushWithContext)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(PushWithContext)
+
+ MOTH_BEGIN_INSTR(PopContext)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(PopContext)
+
+ MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ MOTH_END_INSTR(ForeachIteratorObject)
+
+ MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ MOTH_END_INSTR(ForeachNextPropertyName)
+
+ MOTH_BEGIN_INSTR(DeleteMember)
+ d << dumpRegister(base, nFormals) << "[" << member << "]";
+ MOTH_END_INSTR(DeleteMember)
+
+ MOTH_BEGIN_INSTR(DeleteSubscript)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
+ MOTH_END_INSTR(DeleteSubscript)
+
+ MOTH_BEGIN_INSTR(DeleteName)
+ d << name;
+ MOTH_END_INSTR(DeleteName)
+
+ MOTH_BEGIN_INSTR(TypeofName)
+ d << name;
+ MOTH_END_INSTR(TypeofName)
+
+ MOTH_BEGIN_INSTR(TypeofValue)
+ MOTH_END_INSTR(TypeofValue)
+
+ MOTH_BEGIN_INSTR(DeclareVar)
+ d << isDeletable << ", " << varName;
+ MOTH_END_INSTR(DeclareVar)
+
+ MOTH_BEGIN_INSTR(DefineArray)
+ d << dumpRegister(args, nFormals) << ", " << argc;
+ MOTH_END_INSTR(DefineArray)
+
+ MOTH_BEGIN_INSTR(DefineObjectLiteral)
+ d << dumpRegister(args, nFormals)
+ << ", " << internalClassId
+ << ", " << arrayValueCount
+ << ", " << arrayGetterSetterCountAndFlags;
+ MOTH_END_INSTR(DefineObjectLiteral)
+
+ MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
+ MOTH_END_INSTR(CreateMappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(ConvertThisToObject)
+ MOTH_END_INSTR(ConvertThisToObject)
+
+ MOTH_BEGIN_INSTR(Construct)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(Jump)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Jump)
+
+ MOTH_BEGIN_INSTR(JumpTrue)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpTrue)
+
+ MOTH_BEGIN_INSTR(JumpFalse)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpFalse)
+
+ MOTH_BEGIN_INSTR(CmpEqNull)
+ MOTH_END_INSTR(CmpEqNull)
+
+ MOTH_BEGIN_INSTR(CmpNeNull)
+ MOTH_END_INSTR(CmpNeNull)
+
+ MOTH_BEGIN_INSTR(CmpEqInt)
+ d << lhs;
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNeInt)
+ d << lhs;
+ MOTH_END_INSTR(CmpNeInt)
+
+ MOTH_BEGIN_INSTR(CmpEq)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpNe)
+
+ MOTH_BEGIN_INSTR(CmpGt)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpGt)
+
+ MOTH_BEGIN_INSTR(CmpGe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpGe)
+
+ MOTH_BEGIN_INSTR(CmpLt)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpLt)
+
+ MOTH_BEGIN_INSTR(CmpLe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpLe)
+
+ MOTH_BEGIN_INSTR(CmpStrictEqual)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpStrictEqual)
+
+ MOTH_BEGIN_INSTR(CmpStrictNotEqual)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpStrictNotEqual)
+
+ MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
+ d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
+
+ MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
+ d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
+
+ MOTH_BEGIN_INSTR(UNot)
+ MOTH_END_INSTR(UNot)
+
+ MOTH_BEGIN_INSTR(UPlus)
+ MOTH_END_INSTR(UPlus)
+
+ MOTH_BEGIN_INSTR(UMinus)
+ MOTH_END_INSTR(UMinus)
+
+ MOTH_BEGIN_INSTR(UCompl)
+ MOTH_END_INSTR(UCompl)
+
+ MOTH_BEGIN_INSTR(Increment)
+ MOTH_END_INSTR(PreIncrement)
+
+ MOTH_BEGIN_INSTR(Decrement)
+ MOTH_END_INSTR(PreDecrement)
+
+ MOTH_BEGIN_INSTR(Add)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Add)
+
+ MOTH_BEGIN_INSTR(BitAnd)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitAnd)
+
+ MOTH_BEGIN_INSTR(BitOr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXor)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(UShr)
+
+ MOTH_BEGIN_INSTR(Shr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Shr)
+
+ MOTH_BEGIN_INSTR(Shl)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Shl)
+
+ MOTH_BEGIN_INSTR(BitAndConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitAndConst)
+
+ MOTH_BEGIN_INSTR(BitOrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXorConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(UShrConst)
+
+ MOTH_BEGIN_INSTR(ShrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(ShrConst)
+
+ MOTH_BEGIN_INSTR(ShlConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(ShlConst)
+
+ MOTH_BEGIN_INSTR(Mul)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Mul)
+
+ MOTH_BEGIN_INSTR(Div)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Div)
+
+ MOTH_BEGIN_INSTR(Mod)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Mod)
+
+ MOTH_BEGIN_INSTR(Sub)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Sub)
+
+ MOTH_BEGIN_INSTR(CmpIn)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(CmpIn)
+
+ MOTH_BEGIN_INSTR(CmpInstanceOf)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(CmpInstanceOf)
+
+ MOTH_BEGIN_INSTR(Ret)
+ MOTH_END_INSTR(Ret)
+
+#ifndef QT_NO_QML_DEBUGGER
+ MOTH_BEGIN_INSTR(Debug)
+ MOTH_END_INSTR(Debug)
+#endif // QT_NO_QML_DEBUGGER
+
+ MOTH_BEGIN_INSTR(LoadQmlContext)
+ d << dumpRegister(result, nFormals);
+ MOTH_END_INSTR(LoadQmlContext)
+
+ MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
+ d << dumpRegister(result, nFormals);
+ MOTH_END_INSTR(LoadQmlImportedScripts)
+
+ MOTH_BEGIN_INSTR(LoadQmlSingleton)
+ d << name;
+ MOTH_END_INSTR(LoadQmlSingleton)
+ }
+}
+
+}
+}
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 1bdb8df414..e0b013eb95 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -64,851 +64,451 @@ QT_BEGIN_NAMESPACE
#define MOTH_DEBUG_INSTR(F)
#else
#define MOTH_DEBUG_INSTR(F) \
- F(Line, line) \
- F(Debug, debug)
+ F(Debug)
#endif
+#define INSTRUCTION(op, name, nargs, ...) \
+ op##_INSTRUCTION(name, nargs, __VA_ARGS__)
+
+/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */
+#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0)
+#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0)
+#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index)
+#define INSTR_LoadZero(op) INSTRUCTION(op, LoadZero, 0)
+#define INSTR_LoadTrue(op) INSTRUCTION(op, LoadTrue, 0)
+#define INSTR_LoadFalse(op) INSTRUCTION(op, LoadFalse, 0)
+#define INSTR_LoadNull(op) INSTRUCTION(op, LoadNull, 0)
+#define INSTR_LoadUndefined(op) INSTRUCTION(op, LoadUndefined, 0)
+#define INSTR_LoadInt(op) INSTRUCTION(op, LoadInt, 1, value)
+#define INSTR_MoveConst(op) INSTRUCTION(op, MoveConst, 2, constIndex, destTemp)
+#define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg)
+#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg)
+#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg)
+#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index)
+#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index)
+#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index)
+#define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index)
+#define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId)
+#define INSTR_LoadRegExp(op) INSTRUCTION(op, LoadRegExp, 1, regExpId)
+#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value)
+#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name)
+#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index)
+#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
+#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
+#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base)
+#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base)
+#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index)
+#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
+#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
+#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base)
+#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base)
+#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
+#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
+#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, index)
+#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base)
+#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index)
+#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 2, argc, argv)
+#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv)
+#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv)
+#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv)
+#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv)
+#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv)
+#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv)
+#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset)
+#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
+#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
+#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
+#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
+#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg)
+#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
+#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
+#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0)
+#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0)
+#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base)
+#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index)
+#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name)
+#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name)
+#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0)
+#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable)
+#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args)
+// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean
+#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args)
+#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0)
+#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0)
+#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0)
+#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
+#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
+#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
+#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
+#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
+#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
+#define INSTR_CmpNeInt(op) INSTRUCTION(op, CmpNeInt, 1, lhs)
+#define INSTR_CmpEq(op) INSTRUCTION(op, CmpEq, 1, lhs)
+#define INSTR_CmpNe(op) INSTRUCTION(op, CmpNe, 1, lhs)
+#define INSTR_CmpGt(op) INSTRUCTION(op, CmpGt, 1, lhs)
+#define INSTR_CmpGe(op) INSTRUCTION(op, CmpGe, 1, lhs)
+#define INSTR_CmpLt(op) INSTRUCTION(op, CmpLt, 1, lhs)
+#define INSTR_CmpLe(op) INSTRUCTION(op, CmpLe, 1, lhs)
+#define INSTR_CmpStrictEqual(op) INSTRUCTION(op, CmpStrictEqual, 1, lhs)
+#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
+#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
+#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
+#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
+#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
+#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
+#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
+#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
+#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0)
+#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0)
+#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0)
+#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs)
+#define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs)
+#define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs)
+#define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs)
+#define INSTR_UShr(op) INSTRUCTION(op, UShr, 1, lhs)
+#define INSTR_Shr(op) INSTRUCTION(op, Shr, 1, lhs)
+#define INSTR_Shl(op) INSTRUCTION(op, Shl, 1, lhs)
+#define INSTR_BitAndConst(op) INSTRUCTION(op, BitAndConst, 1, rhs)
+#define INSTR_BitOrConst(op) INSTRUCTION(op, BitOrConst, 1, rhs)
+#define INSTR_BitXorConst(op) INSTRUCTION(op, BitXorConst, 1, rhs)
+#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs)
+#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
+#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
+#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs)
+#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
+#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs)
+#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs)
+#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
+#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
+#define INSTR_LoadQmlSingleton(op) INSTRUCTION(op, LoadQmlSingleton, 1, name)
+
+
#define FOR_EACH_MOTH_INSTR(F) \
- F(Ret, ret) \
- MOTH_DEBUG_INSTR(F) \
- F(LoadRuntimeString, loadRuntimeString) \
- F(LoadRegExp, loadRegExp) \
- F(LoadClosure, loadClosure) \
- F(Move, move) \
- F(MoveConst, moveConst) \
- F(SwapTemps, swapTemps) \
- F(LoadName, loadName) \
- F(GetGlobalLookup, getGlobalLookup) \
- F(StoreName, storeName) \
- F(LoadElement, loadElement) \
- F(LoadElementLookup, loadElementLookup) \
- F(StoreElement, storeElement) \
- F(StoreElementLookup, storeElementLookup) \
- F(LoadProperty, loadProperty) \
- F(GetLookup, getLookup) \
- F(StoreProperty, storeProperty) \
- F(SetLookup, setLookup) \
- F(StoreQObjectProperty, storeQObjectProperty) \
- F(LoadQObjectProperty, loadQObjectProperty) \
- F(StoreScopeObjectProperty, storeScopeObjectProperty) \
- F(StoreContextObjectProperty, storeContextObjectProperty) \
- F(LoadScopeObjectProperty, loadScopeObjectProperty) \
- F(LoadContextObjectProperty, loadContextObjectProperty) \
- F(LoadIdObject, loadIdObject) \
- F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \
- F(LoadSingletonQObjectProperty, loadQObjectProperty) \
- F(Push, push) \
- F(CallValue, callValue) \
- F(CallProperty, callProperty) \
- F(CallPropertyLookup, callPropertyLookup) \
- F(CallScopeObjectProperty, callScopeObjectProperty) \
- F(CallContextObjectProperty, callContextObjectProperty) \
- F(CallElement, callElement) \
- F(CallActivationProperty, callActivationProperty) \
- F(CallGlobalLookup, callGlobalLookup) \
- F(SetExceptionHandler, setExceptionHandler) \
- F(CallBuiltinThrow, callBuiltinThrow) \
- F(CallBuiltinUnwindException, callBuiltinUnwindException) \
- F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \
- F(CallBuiltinPushScope, callBuiltinPushScope) \
- F(CallBuiltinPopScope, callBuiltinPopScope) \
- F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \
- F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \
- F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \
- F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \
- F(CallBuiltinDeleteName, callBuiltinDeleteName) \
- F(CallBuiltinTypeofScopeObjectProperty, callBuiltinTypeofScopeObjectProperty) \
- F(CallBuiltinTypeofContextObjectProperty, callBuiltinTypeofContextObjectProperty) \
- F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \
- F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \
- F(CallBuiltinTypeofName, callBuiltinTypeofName) \
- F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \
- F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \
- F(CallBuiltinDefineArray, callBuiltinDefineArray) \
- F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \
- F(CallBuiltinSetupArgumentsObject, callBuiltinSetupArgumentsObject) \
- F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \
- F(CreateValue, createValue) \
- F(CreateProperty, createProperty) \
- F(ConstructPropertyLookup, constructPropertyLookup) \
- F(CreateActivationProperty, createActivationProperty) \
- F(ConstructGlobalLookup, constructGlobalLookup) \
- F(Jump, jump) \
- F(JumpEq, jumpEq) \
- F(JumpNe, jumpNe) \
- F(UNot, unot) \
- F(UNotBool, unotBool) \
- F(UPlus, uplus) \
- F(UMinus, uminus) \
- F(UCompl, ucompl) \
- F(UComplInt, ucomplInt) \
- F(Increment, increment) \
- F(Decrement, decrement) \
- F(Binop, binop) \
- F(Add, add) \
- F(BitAnd, bitAnd) \
- F(BitOr, bitOr) \
- F(BitXor, bitXor) \
- F(Shr, shr) \
- F(Shl, shl) \
- F(BitAndConst, bitAndConst) \
- F(BitOrConst, bitOrConst) \
- F(BitXorConst, bitXorConst) \
- F(ShrConst, shrConst) \
- F(ShlConst, shlConst) \
- F(Mul, mul) \
- F(Sub, sub) \
- F(BinopContext, binopContext) \
- F(LoadThis, loadThis) \
- F(LoadQmlContext, loadQmlContext) \
- F(LoadQmlImportedScripts, loadQmlImportedScripts) \
- F(LoadQmlSingleton, loadQmlSingleton)
+ F(Ret) \
+ F(Debug) \
+ F(LoadConst) \
+ F(LoadZero) \
+ F(LoadTrue) \
+ F(LoadFalse) \
+ F(LoadNull) \
+ F(LoadUndefined) \
+ F(LoadInt) \
+ F(MoveConst) \
+ F(LoadReg) \
+ F(StoreReg) \
+ F(MoveReg) \
+ F(LoadLocal) \
+ F(StoreLocal) \
+ F(LoadScopedLocal) \
+ F(StoreScopedLocal) \
+ F(LoadRuntimeString) \
+ F(LoadRegExp) \
+ F(LoadClosure) \
+ F(LoadName) \
+ F(LoadGlobalLookup) \
+ F(StoreNameSloppy) \
+ F(StoreNameStrict) \
+ F(LoadElement) \
+ F(LoadElementA) \
+ F(StoreElement) \
+ F(LoadProperty) \
+ F(LoadPropertyA) \
+ F(GetLookup) \
+ F(GetLookupA) \
+ F(StoreProperty) \
+ F(SetLookup) \
+ F(StoreScopeObjectProperty) \
+ F(StoreContextObjectProperty) \
+ F(LoadScopeObjectProperty) \
+ F(LoadContextObjectProperty) \
+ F(LoadIdObject) \
+ F(CallValue) \
+ F(CallProperty) \
+ F(CallPropertyLookup) \
+ F(CallElement) \
+ F(CallName) \
+ F(CallPossiblyDirectEval) \
+ F(CallGlobalLookup) \
+ F(SetExceptionHandler) \
+ F(ThrowException) \
+ F(GetException) \
+ F(SetException) \
+ F(CreateCallContext) \
+ F(PushCatchContext) \
+ F(PushWithContext) \
+ F(PopContext) \
+ F(ForeachIteratorObject) \
+ F(ForeachNextPropertyName) \
+ F(DeleteMember) \
+ F(DeleteSubscript) \
+ F(DeleteName) \
+ F(TypeofName) \
+ F(TypeofValue) \
+ F(DeclareVar) \
+ F(DefineArray) \
+ F(DefineObjectLiteral) \
+ F(CreateMappedArgumentsObject) \
+ F(CreateUnmappedArgumentsObject) \
+ F(ConvertThisToObject) \
+ F(Construct) \
+ F(Jump) \
+ F(JumpTrue) \
+ F(JumpFalse) \
+ F(CmpEqNull) \
+ F(CmpNeNull) \
+ F(CmpEqInt) \
+ F(CmpNeInt) \
+ F(CmpEq) \
+ F(CmpNe) \
+ F(CmpGt) \
+ F(CmpGe) \
+ F(CmpLt) \
+ F(CmpLe) \
+ F(CmpStrictEqual) \
+ F(CmpStrictNotEqual) \
+ F(CmpIn) \
+ F(CmpInstanceOf) \
+ F(JumpStrictEqualStackSlotInt) \
+ F(JumpStrictNotEqualStackSlotInt) \
+ F(UNot) \
+ F(UPlus) \
+ F(UMinus) \
+ F(UCompl) \
+ F(Increment) \
+ F(Decrement) \
+ F(Add) \
+ F(BitAnd) \
+ F(BitOr) \
+ F(BitXor) \
+ F(UShr) \
+ F(Shr) \
+ F(Shl) \
+ F(BitAndConst) \
+ F(BitOrConst) \
+ F(BitXorConst) \
+ F(UShrConst) \
+ F(ShrConst) \
+ F(ShlConst) \
+ F(Mul) \
+ F(Div) \
+ F(Mod) \
+ F(Sub) \
+ F(LoadQmlContext) \
+ F(LoadQmlImportedScripts) \
+ F(LoadQmlSingleton)
+#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlSingleton) + 1)
#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200)
-# define MOTH_THREADED_INTERPRETER
+# define MOTH_COMPUTED_GOTO
#endif
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#define MOTH_INSTR_HEADER quint32 instructionType;
+#define MOTH_INSTR_ENUM(I) I,
+#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I))
+
+#define MOTH_EXPAND_FOR_MSVC(x) x
+#define MOTH_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_DEFINE_ARGS0()
+#define MOTH_DEFINE_ARGS1(arg) \
+ int arg;
+#define MOTH_DEFINE_ARGS2(arg1, arg2) \
+ int arg1; \
+ int arg2;
+#define MOTH_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1; \
+ int arg2; \
+ int arg3;
+#define MOTH_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1; \
+ int arg2; \
+ int arg3; \
+ int arg4;
+
+#define MOTH_COLLECT_ENUMS(instr) \
+ INSTR_##instr(MOTH_GET_ENUM)
+#define MOTH_GET_ENUM_INSTRUCTION(name, ...) \
+ name,
+
+#define MOTH_EMIT_STRUCTS(instr) \
+ INSTR_##instr(MOTH_EMIT_STRUCT)
+#define MOTH_EMIT_STRUCT_INSTRUCTION(name, nargs, ...) \
+ struct instr_##name { \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ };
+
+#define MOTH_EMIT_INSTR_MEMBERS(instr) \
+ INSTR_##instr(MOTH_EMIT_INSTR_MEMBER)
+#define MOTH_EMIT_INSTR_MEMBER_INSTRUCTION(name, nargs, ...) \
+ instr_##name name;
+
+#define MOTH_COLLECT_NARGS(instr) \
+ INSTR_##instr(MOTH_COLLECT_ARG_COUNT)
+#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \
+ nargs,
+
+#define MOTH_DECODE_ARG(arg, type, nargs, offset) \
+ arg = reinterpret_cast<const type *>(code)[-nargs + offset];
+#define MOTH_ADJUST_CODE(type, nargs) \
+ code += static_cast<quintptr>(nargs*sizeof(type) + 1)
+
+#define MOTH_DECODE_INSTRUCTION(name, nargs, ...) \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ op_int_##name: \
+ MOTH_ADJUST_CODE(int, nargs); \
+ MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \
+ goto op_main_##name; \
+ op_char_##name: \
+ MOTH_ADJUST_CODE(char, nargs); \
+ MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \
+ op_main_##name: \
+ ; \
+
+#define MOTH_DECODE_WITH_BASE_INSTRUCTION(name, nargs, ...) \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ const char *base_ptr; \
+ op_int_##name: \
+ base_ptr = code; \
+ MOTH_ADJUST_CODE(int, nargs); \
+ MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \
+ goto op_main_##name; \
+ op_char_##name: \
+ base_ptr = code; \
+ MOTH_ADJUST_CODE(char, nargs); \
+ MOTH_DECODE_ARGS(name, char, nargs, __VA_ARGS__) \
+ op_main_##name: \
+ ; \
+
+#define MOTH_DECODE_ARGS(name, type, nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_DECODE_ARGS##nargs(name, type, nargs, __VA_ARGS__))
+
+#define MOTH_DECODE_ARGS0(name, type, nargs, dummy)
+#define MOTH_DECODE_ARGS1(name, type, nargs, arg) \
+ MOTH_DECODE_ARG(arg, type, nargs, 0);
+#define MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2) \
+ MOTH_DECODE_ARGS1(name, type, nargs, arg1); \
+ MOTH_DECODE_ARG(arg2, type, nargs, 1);
+#define MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3) \
+ MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2); \
+ MOTH_DECODE_ARG(arg3, type, nargs, 2);
+#define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \
+ MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \
+ MOTH_DECODE_ARG(arg4, type, nargs, 3);
+
+#ifdef MOTH_COMPUTED_GOTO
+/* collect jump labels */
+#define COLLECT_LABELS(instr) \
+ INSTR_##instr(GET_LABEL)
+#define GET_LABEL_INSTRUCTION(name, ...) \
+ &&op_char_##name,
+#define COLLECT_LABELS_WIDE(instr) \
+ INSTR_##instr(GET_LABEL_WIDE)
+#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \
+ &&op_int_##name,
+
+#define MOTH_JUMP_TABLE \
+ static const void *jumpTable[] = { \
+ FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \
+ FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \
+ };
+
+#define MOTH_DISPATCH() \
+ goto *jumpTable[*reinterpret_cast<const uchar *>(code)];
+#else
+#define MOTH_JUMP_TABLE
+
+#define MOTH_INSTR_CASE_AND_JUMP(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP)
+#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
+ case static_cast<uchar>(Instr::Type::name): goto op_char_##name;
+#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP_WIDE)
+#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \
+ case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name;
+
+#define MOTH_DISPATCH() \
+ switch (static_cast<uchar>(*code)) { \
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \
+ }
+#endif
-#define MOTH_INSTR_ENUM(I, FMT) I,
-#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK)
+namespace QV4 {
+namespace CompiledData {
+struct CodeOffsetToLine;
+}
-namespace QV4 {
namespace Moth {
- // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
-
-struct Param {
- // Params are looked up as follows:
- // Constant: 0
- // Temp: 1
- // Argument: 2
- // Local: 3
- // Arg(outer): 4
- // Local(outer): 5
- // ...
- unsigned scope : 12;
- unsigned index : 20;
-
- bool isConstant() const { return !scope; }
- bool isArgument() const { return scope >= 2 && !(scope &1); }
- bool isLocal() const { return scope == 3; }
- bool isTemp() const { return scope == 1; }
- bool isScopedLocal() const { return scope >= 3 && (scope & 1); }
-
- static Param createConstant(int index)
- {
- Param p;
- p.scope = 0;
- p.index = index;
- return p;
- }
+class StackSlot {
+ int index;
- static Param createArgument(unsigned idx, uint scope)
- {
- Param p;
- p.scope = 2 + 2*scope;
- p.index = idx;
- return p;
+public:
+ static StackSlot createRegister(int index) {
+ Q_ASSERT(index >= 0);
+ StackSlot t;
+ t.index = index;
+ return t;
}
- static Param createLocal(unsigned idx)
- {
- Param p;
- p.scope = 3;
- p.index = idx;
- return p;
- }
+ int stackSlot() const { return index; }
+ operator int() const { return index; }
+};
- static Param createTemp(unsigned idx)
- {
- Param p;
- p.scope = 1;
- p.index = idx;
- return p;
- }
+inline bool operator==(const StackSlot &l, const StackSlot &r) { return l.stackSlot() == r.stackSlot(); }
+inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackSlot() != r.stackSlot(); }
- static Param createScopedLocal(unsigned idx, uint scope)
- {
- Param p;
- p.scope = 3 + 2*scope;
- p.index = idx;
- return p;
- }
+// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
- inline bool operator==(const Param &other) const
- { return scope == other.scope && index == other.index; }
-
- inline bool operator!=(const Param &other) const
- { return !(*this == other); }
-};
+void dumpConstantTable(const Value *constants, uint count);
+void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>());
+inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1,
+ const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()) {
+ dumpBytecode(bytecode.constData(), bytecode.length(), nLocals, nFormals, startLine, lineNumberMapping);
+}
union Instr
{
- enum Type {
+ enum class Type {
FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
- LastInstruction
};
- struct instr_common {
- MOTH_INSTR_HEADER
- };
- struct instr_ret {
- MOTH_INSTR_HEADER
- Param result;
- };
-
-#ifndef QT_NO_QML_DEBUGGING
- struct instr_line {
- MOTH_INSTR_HEADER
- qint32 lineNumber;
- };
- struct instr_debug {
- MOTH_INSTR_HEADER
- qint32 lineNumber;
- };
-#endif // QT_NO_QML_DEBUGGING
+ FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS)
- struct instr_loadRuntimeString {
- MOTH_INSTR_HEADER
- int stringId;
- Param result;
- };
- struct instr_loadRegExp {
- MOTH_INSTR_HEADER
- int regExpId;
- Param result;
- };
- struct instr_move {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_moveConst {
- MOTH_INSTR_HEADER
- QV4::ReturnedValue source;
- Param result;
- };
- struct instr_swapTemps {
- MOTH_INSTR_HEADER
- Param left;
- Param right;
- };
- struct instr_loadClosure {
- MOTH_INSTR_HEADER
- int value;
- Param result;
- };
- struct instr_loadName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_getGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- Param result;
- };
- struct instr_storeName {
- MOTH_INSTR_HEADER
- int name;
- Param source;
- };
- struct instr_loadProperty {
- MOTH_INSTR_HEADER
- int name;
- Param base;
- Param result;
- };
- struct instr_getLookup {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_loadScopeObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadContextObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadIdObject {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_loadQObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadAttachedQObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param result;
- int attachedPropertiesId;
- };
- struct instr_storeProperty {
- MOTH_INSTR_HEADER
- int name;
- Param base;
- Param source;
- };
- struct instr_setLookup {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param source;
- };
- struct instr_storeScopeObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_storeContextObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_storeQObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_loadElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_loadElementLookup {
- MOTH_INSTR_HEADER
- uint lookup;
- Param base;
- Param index;
- Param result;
- };
- struct instr_storeElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param source;
- };
- struct instr_storeElementLookup {
- MOTH_INSTR_HEADER
- uint lookup;
- Param base;
- Param index;
- Param source;
- };
- struct instr_push {
- MOTH_INSTR_HEADER
- quint32 value;
- };
- struct instr_callValue {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 callData;
- Param dest;
- Param result;
- };
- struct instr_callProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callPropertyLookup {
- MOTH_INSTR_HEADER
- int lookupIndex;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callScopeObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callContextObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_callActivationProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_callGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_setExceptionHandler {
- MOTH_INSTR_HEADER
- qptrdiff offset;
- };
- struct instr_callBuiltinThrow {
- MOTH_INSTR_HEADER
- Param arg;
- };
- struct instr_callBuiltinUnwindException {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_callBuiltinPushCatchScope {
- MOTH_INSTR_HEADER
- int name;
- };
- struct instr_callBuiltinPushScope {
- MOTH_INSTR_HEADER
- Param arg;
- };
- struct instr_callBuiltinPopScope {
- MOTH_INSTR_HEADER
- };
- struct instr_callBuiltinForeachIteratorObject {
- MOTH_INSTR_HEADER
- Param arg;
- Param result;
- };
- struct instr_callBuiltinForeachNextPropertyName {
- MOTH_INSTR_HEADER
- Param arg;
- Param result;
- };
- struct instr_callBuiltinDeleteMember {
- MOTH_INSTR_HEADER
- int member;
- Param base;
- Param result;
- };
- struct instr_callBuiltinDeleteSubscript {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_callBuiltinDeleteName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_callBuiltinTypeofScopeObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofContextObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofMember {
- MOTH_INSTR_HEADER
- int member;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofSubscript {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_callBuiltinTypeofName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_callBuiltinTypeofValue {
- MOTH_INSTR_HEADER
- Param value;
- Param result;
- };
- struct instr_callBuiltinDeclareVar {
- MOTH_INSTR_HEADER
- int varName;
- bool isDeletable;
- };
- struct instr_callBuiltinDefineArray {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 args;
- Param result;
- };
- struct instr_callBuiltinDefineObjectLiteral {
- MOTH_INSTR_HEADER
- int internalClassId;
- uint arrayValueCount;
- uint arrayGetterSetterCountAndFlags; // 30 bits for count, 1 bit for needsSparseArray boolean
- quint32 args;
- Param result;
- };
- struct instr_callBuiltinSetupArgumentsObject {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_callBuiltinConvertThisToObject {
- MOTH_INSTR_HEADER
- };
- struct instr_createValue {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 callData;
- Param func;
- Param result;
- };
- struct instr_createProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_constructPropertyLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_createActivationProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_constructGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_jump {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- };
- struct instr_jumpEq {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- Param condition;
- };
- struct instr_jumpNe {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- Param condition;
- };
- struct instr_unot {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_unotBool {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_uplus {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_uminus {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_ucompl {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_ucomplInt {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_increment {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_decrement {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_binop {
- MOTH_INSTR_HEADER
- int alu; // QV4::Runtime::RuntimeMethods enum value
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_add {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitAnd {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitOr {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitXor {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_shr {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_shl {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitAndConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_bitOrConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_bitXorConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_shrConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_shlConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_mul {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_sub {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_binopContext {
- MOTH_INSTR_HEADER
- uint alu; // offset inside the runtime methods
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_loadThis {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlContext {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlImportedScripts {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlSingleton {
- MOTH_INSTR_HEADER
- Param result;
- int name;
- };
+ FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS)
+};
- instr_common common;
- instr_ret ret;
- instr_line line;
- instr_debug debug;
- instr_loadRuntimeString loadRuntimeString;
- instr_loadRegExp loadRegExp;
- instr_move move;
- instr_moveConst moveConst;
- instr_swapTemps swapTemps;
- instr_loadClosure loadClosure;
- instr_loadName loadName;
- instr_getGlobalLookup getGlobalLookup;
- instr_storeName storeName;
- instr_loadElement loadElement;
- instr_loadElementLookup loadElementLookup;
- instr_storeElement storeElement;
- instr_storeElementLookup storeElementLookup;
- instr_loadProperty loadProperty;
- instr_getLookup getLookup;
- instr_loadScopeObjectProperty loadScopeObjectProperty;
- instr_loadContextObjectProperty loadContextObjectProperty;
- instr_loadIdObject loadIdObject;
- instr_loadQObjectProperty loadQObjectProperty;
- instr_loadAttachedQObjectProperty loadAttachedQObjectProperty;
- instr_storeProperty storeProperty;
- instr_setLookup setLookup;
- instr_storeScopeObjectProperty storeScopeObjectProperty;
- instr_storeContextObjectProperty storeContextObjectProperty;
- instr_storeQObjectProperty storeQObjectProperty;
- instr_push push;
- instr_callValue callValue;
- instr_callProperty callProperty;
- instr_callPropertyLookup callPropertyLookup;
- instr_callScopeObjectProperty callScopeObjectProperty;
- instr_callContextObjectProperty callContextObjectProperty;
- instr_callElement callElement;
- instr_callActivationProperty callActivationProperty;
- instr_callGlobalLookup callGlobalLookup;
- instr_callBuiltinThrow callBuiltinThrow;
- instr_setExceptionHandler setExceptionHandler;
- instr_callBuiltinUnwindException callBuiltinUnwindException;
- instr_callBuiltinPushCatchScope callBuiltinPushCatchScope;
- instr_callBuiltinPushScope callBuiltinPushScope;
- instr_callBuiltinPopScope callBuiltinPopScope;
- instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject;
- instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName;
- instr_callBuiltinDeleteMember callBuiltinDeleteMember;
- instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript;
- instr_callBuiltinDeleteName callBuiltinDeleteName;
- instr_callBuiltinTypeofScopeObjectProperty callBuiltinTypeofScopeObjectProperty;
- instr_callBuiltinTypeofContextObjectProperty callBuiltinTypeofContextObjectProperty;
- instr_callBuiltinTypeofMember callBuiltinTypeofMember;
- instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript;
- instr_callBuiltinTypeofName callBuiltinTypeofName;
- instr_callBuiltinTypeofValue callBuiltinTypeofValue;
- instr_callBuiltinDeclareVar callBuiltinDeclareVar;
- instr_callBuiltinDefineArray callBuiltinDefineArray;
- instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral;
- instr_callBuiltinSetupArgumentsObject callBuiltinSetupArgumentsObject;
- instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject;
- instr_createValue createValue;
- instr_createProperty createProperty;
- instr_constructPropertyLookup constructPropertyLookup;
- instr_createActivationProperty createActivationProperty;
- instr_constructGlobalLookup constructGlobalLookup;
- instr_jump jump;
- instr_jumpEq jumpEq;
- instr_jumpNe jumpNe;
- instr_unot unot;
- instr_unotBool unotBool;
- instr_uplus uplus;
- instr_uminus uminus;
- instr_ucompl ucompl;
- instr_ucomplInt ucomplInt;
- instr_increment increment;
- instr_decrement decrement;
- instr_binop binop;
- instr_add add;
- instr_bitAnd bitAnd;
- instr_bitOr bitOr;
- instr_bitXor bitXor;
- instr_shr shr;
- instr_shl shl;
- instr_bitAndConst bitAndConst;
- instr_bitOrConst bitOrConst;
- instr_bitXorConst bitXorConst;
- instr_shrConst shrConst;
- instr_shlConst shlConst;
- instr_mul mul;
- instr_sub sub;
- instr_binopContext binopContext;
- instr_loadThis loadThis;
- instr_loadQmlContext loadQmlContext;
- instr_loadQmlImportedScripts loadQmlImportedScripts;
- instr_loadQmlSingleton loadQmlSingleton;
-
- static int size(Type type);
+struct InstrInfo
+{
+ static const int argumentCount[];
+ static int size(Instr::Type type);
};
+Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128);
+
template<int N>
struct InstrMeta {
};
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wuninitialized")
-#define MOTH_INSTR_META_TEMPLATE(I, FMT) \
- template<> struct InstrMeta<(int)Instr::I> { \
- enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \
- typedef Instr::instr_##FMT DataType; \
- static const DataType &data(const Instr &instr) { return instr.FMT; } \
- static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \
- static void setDataNoCommon(Instr &instr, const DataType &v) \
- { memcpy(reinterpret_cast<char *>(&instr.FMT) + sizeof(Instr::instr_common), \
- reinterpret_cast<const char *>(&v) + sizeof(Instr::instr_common), \
- Size - sizeof(Instr::instr_common)); } \
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
+#define MOTH_INSTR_META_TEMPLATE(I) \
+ template<> struct InstrMeta<int(Instr::Type::I)> { \
+ enum { Size = MOTH_INSTR_SIZE(I) }; \
+ typedef Instr::instr_##I DataType; \
+ static const DataType &data(const Instr &instr) { return instr.I; } \
+ static void setData(Instr &instr, const DataType &v) \
+ { memcpy(reinterpret_cast<char *>(&instr.I), \
+ reinterpret_cast<const char *>(&v), \
+ Size); } \
};
FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
@@ -919,6 +519,14 @@ class InstrData : public InstrMeta<InstrType>::DataType
{
};
+struct Instruction {
+#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I;
+FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
+#undef MOTH_INSTR_DATA_TYPEDEF
+private:
+ Instruction();
+};
+
} // namespace Moth
} // namespace QV4
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
deleted file mode 100644
index d25d1733c8..0000000000
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ /dev/null
@@ -1,1522 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4isel_util_p.h"
-#include "qv4isel_moth_p.h"
-#include "qv4ssa_p.h"
-#include <private/qv4compileddata_p.h>
-#include <wtf/MathExtras.h>
-
-#if !defined(V4_BOOTSTRAP)
-#include "qv4vme_moth_p.h"
-#include <private/qv4function_p.h>
-#endif
-
-#undef USE_TYPE_INFO
-
-using namespace QV4;
-using namespace QV4::Moth;
-
-namespace {
-
-inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op)
-{
- switch (op) {
- case IR::OpInvalid:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIfTrue:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpNot:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUMinus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUPlus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpCompl:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpBitAnd:
- return QV4::Runtime::bitAnd;
- case IR::OpBitOr:
- return QV4::Runtime::bitOr;
- case IR::OpBitXor:
- return QV4::Runtime::bitXor;
- case IR::OpAdd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpSub:
- return QV4::Runtime::sub;
- case IR::OpMul:
- return QV4::Runtime::mul;
- case IR::OpDiv:
- return QV4::Runtime::div;
- case IR::OpMod:
- return QV4::Runtime::mod;
- case IR::OpLShift:
- return QV4::Runtime::shl;
- case IR::OpRShift:
- return QV4::Runtime::shr;
- case IR::OpURShift:
- return QV4::Runtime::ushr;
- case IR::OpGt:
- return QV4::Runtime::greaterThan;
- case IR::OpLt:
- return QV4::Runtime::lessThan;
- case IR::OpGe:
- return QV4::Runtime::greaterEqual;
- case IR::OpLe:
- return QV4::Runtime::lessEqual;
- case IR::OpEqual:
- return QV4::Runtime::equal;
- case IR::OpNotEqual:
- return QV4::Runtime::notEqual;
- case IR::OpStrictEqual:
- return QV4::Runtime::strictEqual;
- case IR::OpStrictNotEqual:
- return QV4::Runtime::strictNotEqual;
- case IR::OpInstanceof:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIn:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpAnd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpOr:
- return QV4::Runtime::InvalidRuntimeMethod;
- default:
- Q_ASSERT(!"Unknown AluOp");
- return QV4::Runtime::InvalidRuntimeMethod;
- }
-};
-
-inline bool isNumberType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- case IR::DoubleType:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isIntegerType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isBoolType(IR::Expr *e)
-{
- return (e->type == IR::BoolType);
-}
-
-} // anonymous namespace
-
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
- , qmlEngine(qmlEngine)
- , _block(0)
- , _codeStart(0)
- , _codeNext(0)
- , _codeEnd(0)
- , _currentStatement(0)
- , compilationUnit(new CompilationUnit)
-{
- setUseTypeInference(false);
-}
-
-InstructionSelection::~InstructionSelection()
-{
-}
-
-void InstructionSelection::run(int functionIndex)
-{
- IR::Function *function = irModule->functions[functionIndex];
- IR::BasicBlock *block = 0, *nextBlock = 0;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches;
- QHash<IR::BasicBlock *, ptrdiff_t> addrs;
-
- int codeSize = 4096;
- uchar *codeStart = new uchar[codeSize];
- memset(codeStart, 0, codeSize);
- uchar *codeNext = codeStart;
- uchar *codeEnd = codeStart + codeSize;
-
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- IR::Optimizer opt(_function);
- opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false);
- if (opt.isInSSA()) {
- static const bool doStackSlotAllocation =
- qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION");
-
- if (doStackSlotAllocation) {
- IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function);
- } else {
- opt.convertOutOfSSA();
- ConvertTemps().toStackSlots(_function);
- }
- opt.showMeTheCode(_function, "After stack slot allocation");
- } else {
- ConvertTemps().toStackSlots(_function);
- }
-
- BitVector removableJumps = opt.calculateOptionalJumps();
- qSwap(_removableJumps, removableJumps);
-
- IR::Stmt *cs = 0;
- qSwap(_currentStatement, cs);
-
- int locals = frameSize();
- Q_ASSERT(locals >= 0);
-
- IR::BasicBlock *exceptionHandler = 0;
-
- Instruction::Push push;
- push.value = quint32(locals);
- addInstruction(push);
-
- currentLine = 0;
- const QVector<IR::BasicBlock *> &basicBlocks = _function->basicBlocks();
- for (int i = 0, ei = basicBlocks.size(); i != ei; ++i) {
- blockNeedsDebugInstruction = irModule->debugMode;
- _block = basicBlocks[i];
- _nextBlock = (i < ei - 1) ? basicBlocks[i + 1] : 0;
- _addrs.insert(_block, _codeNext - _codeStart);
-
- if (_block->catchBlock != exceptionHandler) {
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- if (_block->catchBlock) {
- ptrdiff_t loc = addInstruction(set) + (((const char *)&set.offset) - ((const char *)&set));
- _patches[_block->catchBlock].append(loc);
- } else {
- addInstruction(set);
- }
- exceptionHandler = _block->catchBlock;
- } else if (_block->catchBlock == nullptr && _block->index() != 0 && _block->in.isEmpty()) {
- exceptionHandler = nullptr;
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- addInstruction(set);
- }
-
- for (IR::Stmt *s : _block->statements()) {
- _currentStatement = s;
-
- if (s->location.isValid()) {
- if (s->location.startLine != currentLine) {
- blockNeedsDebugInstruction = false;
- currentLine = s->location.startLine;
-#if QT_CONFIG(qml_debug)
- if (irModule->debugMode) {
- Instruction::Debug debug;
- debug.lineNumber = currentLine;
- addInstruction(debug);
- } else {
- Instruction::Line line;
- line.lineNumber = currentLine;
- addInstruction(line);
- }
-#endif
- }
- }
-
- visit(s);
- }
- }
-
- // TODO: patch stack size (the push instruction)
- patchJumpAddresses();
-
- codeRefs.insert(_function, squeezeCode());
-
- qSwap(_currentStatement, cs);
- qSwap(_removableJumps, removableJumps);
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- delete[] codeStart;
-}
-
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep()
-{
- compilationUnit->codeRefs.resize(irModule->functions.size());
- int i = 0;
- for (IR::Function *irFunction : qAsConst(irModule->functions))
- compilationUnit->codeRefs[i++] = codeRefs[irFunction];
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
- result.adopt(compilationUnit.take());
- return result;
-}
-
-void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
-{
- Instruction::CallValue call;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.dest = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result)
-{
- if (useFastLookups) {
- Instruction::CallPropertyLookup call;
- call.base = getParam(base);
- call.lookupIndex = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- // call the property on the loaded base
- Instruction::CallProperty call;
- call.base = getParam(base);
- call.name = registerString(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- }
-}
-
-void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
- IR::Expr *result)
-{
- // call the property on the loaded base
- Instruction::CallElement call;
- call.base = getParam(base);
- call.index = getParam(index);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target)
-{
- // FIXME: do something more useful with this info
- if (target->type & IR::NumberType && !(source->type & IR::NumberType))
- unop(IR::OpUPlus, source, target);
- else
- copyValue(source, target);
-}
-
-void InstructionSelection::constructActivationProperty(IR::Name *func,
- IR::ExprList *args,
- IR::Expr *target)
-{
- if (useFastLookups && func->global) {
- Instruction::ConstructGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateActivationProperty create;
- create.name = registerString(*func->id);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::ConstructPropertyLookup call;
- call.base = getParam(base);
- call.index = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateProperty create;
- create.base = getParam(base);
- create.name = registerString(name);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *target)
-{
- Instruction::CreateValue create;
- create.func = getParam(value);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::loadThisObject(IR::Expr *e)
-{
- Instruction::LoadThis load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlContext(IR::Expr *e)
-{
- Instruction::LoadQmlContext load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlImportedScripts(IR::Expr *e)
-{
- Instruction::LoadQmlImportedScripts load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e)
-{
- Instruction::LoadQmlSingleton load;
- load.result = getResultParam(e);
- load.name = registerString(name);
- addInstruction(load);
-}
-
-void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *e)
-{
- Q_ASSERT(sourceConst);
-
- Instruction::MoveConst move;
- move.source = convertToValue(sourceConst).asReturnedValue();
- move.result = getResultParam(e);
- addInstruction(move);
-}
-
-void InstructionSelection::loadString(const QString &str, IR::Expr *target)
-{
- Instruction::LoadRuntimeString load;
- load.stringId = registerString(str);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
-{
- Instruction::LoadRegExp load;
- load.regExpId = registerRegExp(sourceRegexp);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target)
-{
- if (useFastLookups && name->global) {
- Instruction::GetGlobalLookup load;
- load.index = registerGlobalGetterLookup(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadName load;
- load.name = registerString(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
-{
- Instruction::StoreName store;
- store.source = getParam(source);
- store.name = registerString(targetName);
- addInstruction(store);
-}
-
-void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target)
-{
- int id = closure->value;
- Instruction::LoadClosure load;
- load.value = id;
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::GetLookup load;
- load.base = getParam(base);
- load.index = registerGetterLookup(name);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadProperty load;
- load.base = getParam(base);
- load.name = registerString(name);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
- const QString &targetName)
-{
- if (useFastLookups) {
- Instruction::SetLookup store;
- store.base = getParam(targetBase);
- store.index = registerSetterLookup(targetName);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreProperty store;
- store.base = getParam(targetBase);
- store.name = registerString(targetName);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::StoreScopeObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::StoreContextObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex)
-{
- Instruction::StoreQObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::LoadScopeObjectProperty load;
- load.base = getParam(source);
- load.propertyIndex = index;
- load.captureRequired = captureRequired;
- load.result = getResultParam(target);
- addInstruction(load);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::LoadContextObjectProperty load;
- load.base = getParam(source);
- load.propertyIndex = index;
- load.captureRequired = captureRequired;
- load.result = getResultParam(target);
- addInstruction(load);
- } else if (kind == IR::Member::MemberOfIdObjectsArray) {
- Instruction::LoadIdObject load;
- load.base = getParam(source);
- load.index = index;
- load.result = getResultParam(target);
- addInstruction(load);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target)
-{
- if (attachedPropertiesId != 0) {
- Instruction::LoadAttachedQObjectProperty load;
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.attachedPropertiesId = attachedPropertiesId;
- addInstruction(load);
- } else if (isSingletonProperty) {
- Instruction::LoadSingletonQObjectProperty load;
- load.base = getParam(base);
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.captureRequired = captureRequired;
- addInstruction(load);
- } else {
- Instruction::LoadQObjectProperty load;
- load.base = getParam(base);
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.captureRequired = captureRequired;
- addInstruction(load);
- }
-}
-
-void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
-{
- if (0 && useFastLookups) {
- Instruction::LoadElementLookup load;
- load.lookup = registerIndexedGetterLookup();
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadElement load;
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase,
- IR::Expr *targetIndex)
-{
- if (0 && useFastLookups) {
- Instruction::StoreElementLookup store;
- store.lookup = registerIndexedSetterLookup();
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreElement store;
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target)
-{
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
-}
-
-void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target)
-{
- Instruction::SwapTemps swap;
- swap.left = getParam(source);
- swap.right = getParam(target);
- addInstruction(swap);
-}
-
-void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
-{
- switch (oper) {
- case IR::OpIfTrue:
- Q_ASSERT(!"unreachable"); break;
- case IR::OpNot: {
- // ### enabling this fails in some cases, where apparently the value is not a bool at runtime
- if (0 && isBoolType(source)) {
- Instruction::UNotBool unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UNot unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- case IR::OpUMinus: {
- Instruction::UMinus uminus;
- uminus.source = getParam(source);
- uminus.result = getResultParam(target);
- addInstruction(uminus);
- return;
- }
- case IR::OpUPlus: {
- if (isNumberType(source)) {
- // use a move
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
- return;
- }
- Instruction::UPlus uplus;
- uplus.source = getParam(source);
- uplus.result = getResultParam(target);
- addInstruction(uplus);
- return;
- }
- case IR::OpCompl: {
- // ### enabling this fails in some cases, where apparently the value is not a int at runtime
- if (0 && isIntegerType(source)) {
- Instruction::UComplInt unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UCompl ucompl;
- ucompl.source = getParam(source);
- ucompl.result = getResultParam(target);
- addInstruction(ucompl);
- return;
- }
- case IR::OpIncrement: {
- Instruction::Increment inc;
- inc.source = getParam(source);
- inc.result = getResultParam(target);
- addInstruction(inc);
- return;
- }
- case IR::OpDecrement: {
- Instruction::Decrement dec;
- dec.source = getParam(source);
- dec.result = getResultParam(target);
- addInstruction(dec);
- return;
- }
- default: break;
- } // switch
-
- Q_ASSERT(!"unreachable");
-}
-
-void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- binopHelper(oper, leftSource, rightSource, target);
-}
-
-Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- if (oper == IR::OpAdd) {
- Instruction::Add add;
- add.lhs = getParam(leftSource);
- add.rhs = getParam(rightSource);
- add.result = getResultParam(target);
- addInstruction(add);
- return add.result;
- }
- if (oper == IR::OpSub) {
- Instruction::Sub sub;
- sub.lhs = getParam(leftSource);
- sub.rhs = getParam(rightSource);
- sub.result = getResultParam(target);
- addInstruction(sub);
- return sub.result;
- }
- if (oper == IR::OpMul) {
- Instruction::Mul mul;
- mul.lhs = getParam(leftSource);
- mul.rhs = getParam(rightSource);
- mul.result = getResultParam(target);
- addInstruction(mul);
- return mul.result;
- }
- if (oper == IR::OpBitAnd) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitAndConst bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = convertToValue(c).Value::toInt32();
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- Instruction::BitAnd bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = getParam(rightSource);
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- if (oper == IR::OpBitOr) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitOrConst bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = convertToValue(c).Value::toInt32();
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- Instruction::BitOr bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = getParam(rightSource);
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- if (oper == IR::OpBitXor) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitXorConst bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = convertToValue(c).Value::toInt32();
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- Instruction::BitXor bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = getParam(rightSource);
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- if (oper == IR::OpRShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShrConst shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- Instruction::Shr shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = getParam(rightSource);
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- if (oper == IR::OpLShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShlConst shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
- Instruction::Shl shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = getParam(rightSource);
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
-
- if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
- Instruction::BinopContext binop;
- if (oper == IR::OpInstanceof)
- binop.alu = QV4::Runtime::instanceof;
- else if (oper == IR::OpIn)
- binop.alu = QV4::Runtime::in;
- else
- binop.alu = QV4::Runtime::add;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod);
- addInstruction(binop);
- return binop.result;
- } else {
- auto binopFunc = aluOpFunction(oper);
- Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod);
- Instruction::Binop binop;
- binop.alu = binopFunc;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- addInstruction(binop);
- return binop.result;
- }
-}
-
-void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 *args)
-{
- int argLocation = outgoingArgumentTempStart();
- argc = 0;
- if (args)
- *args = argLocation;
- if (e) {
- // We need to move all the temps into the function arg array
- Q_ASSERT(argLocation >= 0);
- while (e) {
- if (IR::Const *c = e->expr->asConst()) {
- Instruction::MoveConst move;
- move.source = convertToValue(c).asReturnedValue();
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(e->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
- ++argc;
- e = e->next;
- }
- }
-}
-
-void InstructionSelection::addDebugInstruction()
-{
-#if QT_CONFIG(qml_debug)
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
-#endif
-}
-
-void InstructionSelection::visitJump(IR::Jump *s)
-{
- if (s->target == _nextBlock)
- return;
- if (_removableJumps.at(_block->index()))
- return;
-
- addDebugInstruction();
-
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[s->target].append(loc);
-}
-
-void InstructionSelection::visitCJump(IR::CJump *s)
-{
- addDebugInstruction();
-
- Param condition;
- if (IR::Temp *t = s->cond->asTemp()) {
- condition = getResultParam(t);
- } else if (IR::Binop *b = s->cond->asBinop()) {
- condition = binopHelper(b->op, b->left, b->right, /*target*/0);
- } else {
- Q_UNIMPLEMENTED();
- }
-
- if (s->iftrue == _nextBlock) {
- Instruction::JumpNe jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- } else {
- Instruction::JumpEq jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iftrue].append(trueLoc);
-
- if (s->iffalse != _nextBlock) {
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- }
- }
-}
-
-void InstructionSelection::visitRet(IR::Ret *s)
-{
- // this is required so stepOut will always be guaranteed to stop in every stack frame
- addDebugInstruction();
-
- Instruction::Ret ret;
- ret.result = getParam(s->expr);
- addInstruction(ret);
-}
-
-void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result)
-{
- if (useFastLookups && func->global) {
- Instruction::CallGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- return;
- }
- Instruction::CallActivationProperty call;
- call.name = registerString(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallBuiltinTypeofScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallBuiltinTypeofContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofValue call;
- call.value = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result)
-{
- Instruction::MoveConst move;
- move.source = QV4::Encode(false);
- move.result = getResultParam(result);
- addInstruction(move);
-}
-
-void InstructionSelection::callBuiltinThrow(IR::Expr *arg)
-{
- Instruction::CallBuiltinThrow call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinReThrow()
-{
- if (_block->catchBlock) {
- // jump to exception handler
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[_block->catchBlock].append(loc);
- } else {
- Instruction::Ret ret;
- int idx = jsUnitGenerator()->registerConstant(QV4::Encode::undefined());
- ret.result = Param::createConstant(idx);
- addInstruction(ret);
- }
-}
-
-void InstructionSelection::callBuiltinUnwindException(IR::Expr *result)
-{
- Instruction::CallBuiltinUnwindException call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
-{
- Instruction::CallBuiltinPushCatchScope call;
- call.name = registerString(exceptionName);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachIteratorObject call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachNextPropertyName call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg)
-{
- Instruction::CallBuiltinPushScope call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPopScope()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinPopScope call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
-{
- Instruction::CallBuiltinDeclareVar call;
- call.isDeletable = deletable;
- call.varName = registerString(name);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args)
-{
- Instruction::CallBuiltinDefineArray call;
- prepareCallArgs(args, call.argc, &call.args);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray)
-{
- int argLocation = outgoingArgumentTempStart();
-
- const int classId = registerJSClass(keyValuePairCount, keyValuePairs);
-
- // Process key/value pairs first
- IR::ExprList *it = keyValuePairs;
- for (int i = 0; i < keyValuePairCount; ++i, it = it->next) {
- // Skip name
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (IR::Const *c = it->expr->asConst()) {
- Instruction::MoveConst move;
- move.source = convertToValue(c).asReturnedValue();
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
-
- if (!isData) {
- it = it->next;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- }
- }
-
- // Process array values
- uint arrayValueCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (!isData) {
- it = it->next; // getter
- it = it->next; // setter
- continue;
- }
-
- ++arrayValueCount;
-
- Instruction::MoveConst indexMove;
- indexMove.source = convertToValue(index).asReturnedValue();
- indexMove.result = Param::createTemp(argLocation);
- addInstruction(indexMove);
- ++argLocation;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- it = it->next;
- }
-
- // Process array getter/setter pairs
- uint arrayGetterSetterCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (isData) {
- it = it->next; // value
- continue;
- }
-
- ++arrayGetterSetterCount;
-
- Instruction::MoveConst indexMove;
- indexMove.source = convertToValue(index).asReturnedValue();
- indexMove.result = Param::createTemp(argLocation);
- addInstruction(indexMove);
- ++argLocation;
-
- // getter
- Instruction::Move moveGetter;
- moveGetter.source = getParam(it->expr);
- moveGetter.result = Param::createTemp(argLocation);
- addInstruction(moveGetter);
- ++argLocation;
- it = it->next;
-
- // setter
- Instruction::Move moveSetter;
- moveSetter.source = getParam(it->expr);
- moveSetter.result = Param::createTemp(argLocation);
- addInstruction(moveSetter);
- ++argLocation;
- it = it->next;
- }
-
- Instruction::CallBuiltinDefineObjectLiteral call;
- call.internalClassId = classId;
- call.arrayValueCount = arrayValueCount;
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCount | (needSparseArray << 30);
- call.args = outgoingArgumentTempStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result)
-{
- Instruction::CallBuiltinSetupArgumentsObject call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinConvertThisToObject call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
-{
- instr.common.instructionType = type;
-
- int instructionSize = Instr::size(type);
- if (_codeEnd - _codeNext < instructionSize) {
- int currSize = _codeEnd - _codeStart;
- uchar *newCode = new uchar[currSize * 2];
- ::memset(newCode + currSize, 0, currSize);
- ::memcpy(newCode, _codeStart, currSize);
- _codeNext = _codeNext - _codeStart + newCode;
- delete[] _codeStart;
- _codeStart = newCode;
- _codeEnd = _codeStart + currSize * 2;
- }
-
- ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize);
- ptrdiff_t ptrOffset = _codeNext - _codeStart;
- _codeNext += instructionSize;
-
- return ptrOffset;
-}
-
-void InstructionSelection::patchJumpAddresses()
-{
- typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt;
- for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) {
- Q_ASSERT(_addrs.contains(i.key()));
- ptrdiff_t target = _addrs.value(i.key());
-
- const QVector<ptrdiff_t> &patchList = i.value();
- for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) {
- ptrdiff_t patch = patchList.at(ii);
-
- *((ptrdiff_t *)(_codeStart + patch)) = target - patch;
- }
- }
-
- _patches.clear();
- _addrs.clear();
-}
-
-QByteArray InstructionSelection::squeezeCode() const
-{
- int codeSize = _codeNext - _codeStart;
- QByteArray squeezed;
- squeezed.resize(codeSize);
- ::memcpy(squeezed.data(), _codeStart, codeSize);
- return squeezed;
-}
-
-Param InstructionSelection::getParam(IR::Expr *e) {
- Q_ASSERT(e);
-
- if (IR::Const *c = e->asConst()) {
- int idx = jsUnitGenerator()->registerConstant(convertToValue(c).asReturnedValue());
- return Param::createConstant(idx);
- } else if (IR::Temp *t = e->asTemp()) {
- switch (t->kind) {
- case IR::Temp::StackSlot:
- return Param::createTemp(t->index);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else if (IR::ArgLocal *al = e->asArgLocal()) {
- switch (al->kind) {
- case IR::ArgLocal::Formal:
- case IR::ArgLocal::ScopedFormal: return Param::createArgument(al->index, al->scope);
- case IR::ArgLocal::Local: return Param::createLocal(al->index);
- case IR::ArgLocal::ScopedLocal: return Param::createScopedLocal(al->index, al->scope);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else {
- Q_UNIMPLEMENTED();
- return Param();
- }
-}
-
-
-CompilationUnit::~CompilationUnit()
-{
-}
-
-#if !defined(V4_BOOTSTRAP)
-
-void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
-{
- runtimeFunctions.resize(data->functionTableSize);
- runtimeFunctions.fill(0);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
-
- QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, &VME::exec);
- runtimeFunction->codeData = reinterpret_cast<const uchar *>(codeRefs.at(i).constData());
- runtimeFunctions[i] = runtimeFunction;
- }
-}
-
-bool CompilationUnit::memoryMapCode(QString *errorString)
-{
- Q_UNUSED(errorString);
- codeRefs.resize(data->functionTableSize);
-
- const char *basePtr = reinterpret_cast<const char *>(data);
-
- for (uint i = 0; i < data->functionTableSize; ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
- const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset));
- QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize);
- codeRefs[i] = code;
- }
-
- return true;
-}
-
-#endif // V4_BOOTSTRAP
-
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
-{
- const int codeAlignment = 16;
- quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
- for (int i = 0; i < codeRefs.size(); ++i) {
- CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
- compiledFunction->codeOffset = offset;
- compiledFunction->codeSize = codeRefs.at(i).size();
- offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
- }
-}
-
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
-{
- Q_ASSERT(device->pos() == unit->unitSize);
- Q_ASSERT(device->atEnd());
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
-
- QByteArray padding;
-
- for (int i = 0; i < codeRefs.size(); ++i) {
- const CompiledData::Function *compiledFunction = unit->functionAt(i);
-
- if (device->pos() > qint64(compiledFunction->codeOffset)) {
- *errorString = QStringLiteral("Invalid state of cache file to write.");
- return false;
- }
-
- const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
- padding.fill(0, paddingSize);
- qint64 written = device->write(padding);
- if (written != padding.size()) {
- *errorString = device->errorString();
- return false;
- }
-
- QByteArray code = codeRefs.at(i);
-
- written = device->write(code.constData(), compiledFunction->codeSize);
- if (written != qint64(compiledFunction->codeSize)) {
- *errorString = device->errorString();
- return false;
- }
- }
- return true;
-}
-
-QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
-{
- QQmlRefPointer<CompiledData::CompilationUnit> result;
- result.adopt(new Moth::CompilationUnit);
- return result;
-}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
deleted file mode 100644
index db49177783..0000000000
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_MOTH_P_H
-#define QV4ISEL_MOTH_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qv4global_p.h>
-#include <private/qv4isel_p.h>
-#include <private/qv4isel_util_p.h>
-#include <private/qv4util_p.h>
-#include <private/qv4jsir_p.h>
-#include <private/qv4value_p.h>
-#include "qv4instr_moth_p.h"
-
-#if !defined(V4_BOOTSTRAP)
-QT_REQUIRE_CONFIG(qml_interpreter);
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace Moth {
-
-struct CompilationUnit : public QV4::CompiledData::CompilationUnit
-{
- virtual ~CompilationUnit();
-#if !defined(V4_BOOTSTRAP)
- void linkBackendToEngine(QV4::ExecutionEngine *engine) override;
- bool memoryMapCode(QString *errorString) override;
-#endif
- void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) override;
- bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) override;
-
- QVector<QByteArray> codeRefs;
-
-};
-
-class Q_QML_EXPORT InstructionSelection:
- public IR::IRDecoder,
- public EvalInstructionSelection
-{
-public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- ~InstructionSelection();
-
- void run(int functionIndex) override;
-
-protected:
- QQmlRefPointer<CompiledData::CompilationUnit> backendCompileStep() override;
-
- void visitJump(IR::Jump *) override;
- void visitCJump(IR::CJump *) override;
- void visitRet(IR::Ret *) override;
-
- void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override;
- void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinTypeofName(const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override;
- void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinDeleteName(const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteValue(IR::Expr *result) override;
- void callBuiltinThrow(IR::Expr *arg) override;
- void callBuiltinReThrow() override;
- void callBuiltinUnwindException(IR::Expr *) override;
- void callBuiltinPushCatchScope(const QString &exceptionName) override;
- void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinPushWithScope(IR::Expr *arg) override;
- void callBuiltinPopScope() override;
- void callBuiltinDeclareVar(bool deletable, const QString &name) override;
- void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override;
- void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override;
- void callBuiltinSetupArgumentObject(IR::Expr *result) override;
- void callBuiltinConvertThisToObject() override;
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override;
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override;
- void convertType(IR::Expr *source, IR::Expr *target) override;
- void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void loadThisObject(IR::Expr *e) override;
- void loadQmlContext(IR::Expr *e) override;
- void loadQmlImportedScripts(IR::Expr *e) override;
- void loadQmlSingleton(const QString &name, IR::Expr *e) override;
- void loadConst(IR::Const *sourceConst, IR::Expr *e) override;
- void loadString(const QString &str, IR::Expr *target) override;
- void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override;
- void getActivationProperty(const IR::Name *name, IR::Expr *target) override;
- void setActivationProperty(IR::Expr *source, const QString &targetName) override;
- void initClosure(IR::Closure *closure, IR::Expr *target) override;
- void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override;
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override;
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override;
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override;
- void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override;
- void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override;
- void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override;
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override;
- void copyValue(IR::Expr *source, IR::Expr *target) override;
- void swapValues(IR::Expr *source, IR::Expr *target) override;
- void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) override;
- void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override;
-
-private:
- Param binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target);
-
- struct Instruction {
-#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I;
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
-#undef MOTH_INSTR_DATA_TYPEDEF
- private:
- Instruction();
- };
-
- Param getParam(IR::Expr *e);
-
- Param getResultParam(IR::Expr *result)
- {
- if (result)
- return getParam(result);
- else
- return Param::createTemp(scratchTempIndex());
- }
-
- void simpleMove(IR::Move *);
- void prepareCallArgs(IR::ExprList *, quint32 &, quint32 * = 0);
-
- int scratchTempIndex() const { return _function->tempCount; }
- int callDataStart() const { return scratchTempIndex() + 1; }
- int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); }
- int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
-
- template <int Instr>
- inline ptrdiff_t addInstruction(const InstrData<Instr> &data);
- inline void addDebugInstruction();
-
- ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr);
- void patchJumpAddresses();
- QByteArray squeezeCode() const;
-
- QQmlEnginePrivate *qmlEngine;
-
- bool blockNeedsDebugInstruction;
- uint currentLine;
- IR::BasicBlock *_block;
- IR::BasicBlock *_nextBlock;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
- QHash<IR::BasicBlock *, ptrdiff_t> _addrs;
-
- uchar *_codeStart;
- uchar *_codeNext;
- uchar *_codeEnd;
-
- BitVector _removableJumps;
- IR::Stmt *_currentStatement;
-
- QScopedPointer<CompilationUnit> compilationUnit;
- QHash<IR::Function *, QByteArray> codeRefs;
-};
-
-class Q_QML_EXPORT ISelFactory: public EvalISelFactory
-{
-public:
- ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
- virtual ~ISelFactory() {}
- EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) override final
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
- bool jitCompileRegexps() const override final
- { return false; }
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() override;
-
-};
-
-template<int InstrT>
-ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data)
-{
- Instr genericInstr;
- InstrMeta<InstrT>::setDataNoCommon(genericInstr, data);
- return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr);
-}
-
-} // namespace Moth
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_MOTH_P_H
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
deleted file mode 100644
index efcfb9bd77..0000000000
--- a/src/qml/compiler/qv4isel_p.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QDebug>
-#include <QtCore/QBuffer>
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4value_p.h>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QString>
-
-using namespace QV4;
-using namespace QV4::IR;
-
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : useFastLookups(true)
- , useTypeInference(true)
- , executableAllocator(execAllocator)
- , irModule(module)
-{
- if (!jsGenerator) {
- jsGenerator = new QV4::Compiler::JSUnitGenerator(module);
- ownJSGenerator.reset(jsGenerator);
- }
- this->jsGenerator = jsGenerator;
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(execAllocator);
-#endif
- Q_ASSERT(module);
- jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
-}
-
-EvalInstructionSelection::~EvalInstructionSelection()
-{}
-
-EvalISelFactory::~EvalISelFactory()
-{}
-
-QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
-{
- for (int i = 0; i < irModule->functions.size(); ++i)
- run(i);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep();
- if (generateUnitData)
- unit->data = jsGenerator->generateUnit();
- return unit;
-}
-
-void IRDecoder::visitMove(IR::Move *s)
-{
- if (IR::Name *n = s->target->asName()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setActivationProperty(s->source, *n->id);
- return;
- }
- } else if (s->target->asTemp() || s->target->asArgLocal()) {
- if (IR::Name *n = s->source->asName()) {
- if (n->id && *n->id == QLatin1String("this")) // TODO: `this' should be a builtin.
- loadThisObject(s->target);
- else if (n->builtin == IR::Name::builtin_qml_context)
- loadQmlContext(s->target);
- else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object)
- loadQmlImportedScripts(s->target);
- else if (n->qmlSingleton)
- loadQmlSingleton(*n->id, s->target);
- else
- getActivationProperty(n, s->target);
- return;
- } else if (IR::Const *c = s->source->asConst()) {
- loadConst(c, s->target);
- return;
- } else if (s->source->asTemp() || s->source->asArgLocal()) {
- if (s->swap)
- swapValues(s->source, s->target);
- else
- copyValue(s->source, s->target);
- return;
- } else if (IR::String *str = s->source->asString()) {
- loadString(*str->value, s->target);
- return;
- } else if (IR::RegExp *re = s->source->asRegExp()) {
- loadRegexp(re, s->target);
- return;
- } else if (IR::Closure *clos = s->source->asClosure()) {
- initClosure(clos, s->target);
- return;
- } else if (IR::New *ctor = s->source->asNew()) {
- if (Name *func = ctor->base->asName()) {
- constructActivationProperty(func, ctor->args, s->target);
- return;
- } else if (IR::Member *member = ctor->base->asMember()) {
- constructProperty(member->base, *member->name, ctor->args, s->target);
- return;
- } else if (ctor->base->asTemp() || ctor->base->asArgLocal()) {
- constructValue(ctor->base, ctor->args, s->target);
- return;
- }
- } else if (IR::Member *m = s->source->asMember()) {
- if (m->property) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- bool captureRequired = true;
-
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject;
-
- if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) {
- if (m->kind == IR::Member::MemberOfQmlContextObject) {
- _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- } else if (m->kind == IR::Member::MemberOfQmlScopeObject) {
- _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- }
- }
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), captureRequired, s->target);
- return;
- }
- getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target);
-#endif // V4_BOOTSTRAP
- return;
- } else if (m->kind == IR::Member::MemberOfIdObjectsArray) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, /*captureRequired*/false, s->target);
- return;
- } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- getProperty(m->base, *m->name, s->target);
- return;
- }
- } else if (IR::Subscript *ss = s->source->asSubscript()) {
- getElement(ss->base, ss->index, s->target);
- return;
- } else if (IR::Unop *u = s->source->asUnop()) {
- unop(u->op, u->expr, s->target);
- return;
- } else if (IR::Binop *b = s->source->asBinop()) {
- binop(b->op, b->left, b->right, s->target);
- return;
- } else if (IR::Call *c = s->source->asCall()) {
- if (c->base->asName()) {
- callBuiltin(c, s->target);
- return;
- } else if (Member *member = c->base->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, s->target);
- return;
- } else if (Subscript *ss = c->base->asSubscript()) {
- callSubscript(ss->base, ss->index, c->args, s->target);
- return;
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, s->target);
- return;
- }
- } else if (IR::Convert *c = s->source->asConvert()) {
- Q_ASSERT(c->expr->asTemp() || c->expr->asArgLocal());
- convertType(c->expr, s->target);
- return;
- }
- } else if (IR::Member *m = s->target->asMember()) {
- if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum);
- Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- if (m->property && attachedPropertiesId == 0) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex());
- return;
- }
- setQObjectProperty(s->source, m->base, m->property->coreIndex());
-#endif
- return;
- } else {
- setProperty(s->source, m->base, *m->name);
- return;
- }
- }
- }
- } else if (IR::Subscript *ss = s->target->asSubscript()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setElement(s->source, ss->base, ss->index);
- return;
- }
- }
-
- // For anything else...:
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(s);
- qout << endl;
- qDebug("%s", buf.data().constData());
- Q_ASSERT(!"TODO");
-}
-
-IRDecoder::~IRDecoder()
-{
-}
-
-void IRDecoder::visitExp(IR::Exp *s)
-{
- if (IR::Call *c = s->expr->asCall()) {
- // These are calls where the result is ignored.
- if (c->base->asName()) {
- callBuiltin(c, 0);
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, 0);
- } else if (Member *member = c->base->asMember()) {
- Q_ASSERT(member->base->asTemp() || member->base->asArgLocal());
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, 0);
- } else if (Subscript *s = c->base->asSubscript()) {
- callSubscript(s->base, s->index, c->args, 0);
- } else {
- Q_UNREACHABLE();
- }
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRDecoder::callBuiltin(IR::Call *call, Expr *result)
-{
- IR::Name *baseName = call->base->asName();
- Q_ASSERT(baseName != 0);
-
- switch (baseName->builtin) {
- case IR::Name::builtin_invalid:
- callBuiltinInvalid(baseName, call->args, result);
- return;
-
- case IR::Name::builtin_typeof: {
- if (IR::Member *member = call->args->expr->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callBuiltinTypeofQmlContextProperty(member->base,
- IR::Member::MemberKind(member->kind),
- member->property->coreIndex(), result);
- return;
- }
-#endif
- callBuiltinTypeofMember(member->base, *member->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinTypeofSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinTypeofName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asConst() ||
- call->args->expr->asArgLocal()) {
- callBuiltinTypeofValue(call->args->expr, result);
- return;
- }
- } break;
-
- case IR::Name::builtin_delete: {
- if (IR::Member *m = call->args->expr->asMember()) {
- callBuiltinDeleteMember(m->base, *m->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinDeleteSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinDeleteName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asArgLocal()) {
- // TODO: should throw in strict mode
- callBuiltinDeleteValue(result);
- return;
- }
- } break;
-
- case IR::Name::builtin_throw: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg->asTemp() || arg->asConst() || arg->asArgLocal());
- callBuiltinThrow(arg);
- } return;
-
- case IR::Name::builtin_rethrow: {
- callBuiltinReThrow();
- } return;
-
- case IR::Name::builtin_unwind_exception: {
- callBuiltinUnwindException(result);
- } return;
-
- case IR::Name::builtin_push_catch_scope: {
- IR::String *s = call->args->expr->asString();
- Q_ASSERT(s);
- callBuiltinPushCatchScope(*s->value);
- } return;
-
- case IR::Name::builtin_foreach_iterator_object: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachIteratorObject(arg, result);
- } return;
-
- case IR::Name::builtin_foreach_next_property_name: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachNextPropertyname(arg, result);
- } return;
- case IR::Name::builtin_push_with_scope: {
- if (call->args->expr->asTemp() || call->args->expr->asArgLocal())
- callBuiltinPushWithScope(call->args->expr);
- else
- Q_UNIMPLEMENTED();
- } return;
-
- case IR::Name::builtin_pop_scope:
- callBuiltinPopScope();
- return;
-
- case IR::Name::builtin_declare_vars: {
- if (!call->args)
- return;
- IR::Const *deletable = call->args->expr->asConst();
- Q_ASSERT(deletable->type == IR::BoolType);
- for (IR::ExprList *it = call->args->next; it; it = it->next) {
- IR::Name *arg = it->expr->asName();
- Q_ASSERT(arg != 0);
- callBuiltinDeclareVar(deletable->value != 0, *arg->id);
- }
- } return;
-
- case IR::Name::builtin_define_array:
- callBuiltinDefineArray(result, call->args);
- return;
-
- case IR::Name::builtin_define_object_literal: {
- IR::ExprList *args = call->args;
- const int keyValuePairsCount = args->expr->asConst()->value;
- args = args->next;
-
- IR::ExprList *keyValuePairs = args;
- for (int i = 0; i < keyValuePairsCount; ++i) {
- args = args->next; // name
- bool isData = args->expr->asConst()->value;
- args = args->next; // isData flag
- args = args->next; // value or getter
- if (!isData)
- args = args->next; // setter
- }
-
- IR::ExprList *arrayEntries = args;
- bool needSparseArray = false;
- for (IR::ExprList *it = arrayEntries; it; it = it->next) {
- uint index = it->expr->asConst()->value;
- if (index > 16) {
- needSparseArray = true;
- break;
- }
- it = it->next;
- bool isData = it->expr->asConst()->value;
- it = it->next;
- if (!isData)
- it = it->next;
- }
-
- callBuiltinDefineObjectLiteral(result, keyValuePairsCount, keyValuePairs, arrayEntries, needSparseArray);
- } return;
-
- case IR::Name::builtin_setup_argument_object:
- callBuiltinSetupArgumentObject(result);
- return;
-
- case IR::Name::builtin_convert_this_to_object:
- callBuiltinConvertThisToObject();
- return;
-
- default:
- break;
- }
-
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(call); qout << endl;
- qDebug("%s", buf.data().constData());
- Q_UNREACHABLE();
-}
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
deleted file mode 100644
index 037c02e5ea..0000000000
--- a/src/qml/compiler/qv4isel_p.h
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_P_H
-#define QV4ISEL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
-#include <private/qv4compileddata_p.h>
-#include <private/qv4compiler_p.h>
-
-#include <qglobal.h>
-#include <QHash>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlEnginePrivate;
-
-namespace QV4 {
-
-class EvalISelFactory;
-class ExecutableAllocator;
-struct Function;
-
-class Q_QML_PRIVATE_EXPORT EvalInstructionSelection
-{
-public:
- EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- virtual ~EvalInstructionSelection() = 0;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
-
- void setUseFastLookups(bool b) { useFastLookups = b; }
- void setUseTypeInference(bool onoff) { useTypeInference = onoff; }
-
- int registerString(const QString &str) { return jsGenerator->registerString(str); }
- uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); }
- uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); }
- uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); }
- uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); }
- uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); }
- int registerRegExp(IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); }
- int registerJSClass(int count, IR::ExprList *args) { return jsGenerator->registerJSClass(count, args); }
- QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; }
-
-protected:
- virtual void run(int functionIndex) = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() = 0;
-
- bool useFastLookups;
- bool useTypeInference;
- QV4::ExecutableAllocator *executableAllocator;
- QV4::Compiler::JSUnitGenerator *jsGenerator;
- QScopedPointer<QV4::Compiler::JSUnitGenerator> ownJSGenerator;
- IR::Module *irModule;
-};
-
-class Q_QML_PRIVATE_EXPORT EvalISelFactory
-{
-public:
- EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {}
- virtual ~EvalISelFactory() = 0;
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
- virtual bool jitCompileRegexps() const = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
-
- const QString codeGeneratorName;
-};
-
-namespace IR {
-class Q_QML_PRIVATE_EXPORT IRDecoder
-{
-public:
- IRDecoder() : _function(0) {}
- virtual ~IRDecoder() = 0;
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private: // visitor methods for StmtVisitor:
- void visitMove(IR::Move *s);
- void visitExp(IR::Exp *s);
-
-public: // to implement by subclasses:
- virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteValue(IR::Expr *result) = 0;
- virtual void callBuiltinThrow(IR::Expr *arg) = 0;
- virtual void callBuiltinReThrow() = 0;
- virtual void callBuiltinUnwindException(IR::Expr *) = 0;
- virtual void callBuiltinPushCatchScope(const QString &exceptionName) = 0;
- virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinPushWithScope(IR::Expr *arg) = 0;
- virtual void callBuiltinPopScope() = 0;
- virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0;
- virtual void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) = 0;
- virtual void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) = 0;
- virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0;
- virtual void callBuiltinConvertThisToObject() = 0;
- virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void convertType(IR::Expr *source, IR::Expr *target) = 0;
- virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void loadThisObject(IR::Expr *target) = 0;
- virtual void loadQmlContext(IR::Expr *target) = 0;
- virtual void loadQmlImportedScripts(IR::Expr *target) = 0;
- virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0;
- virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0;
- virtual void loadString(const QString &str, IR::Expr *target) = 0;
- virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) = 0;
- virtual void getActivationProperty(const IR::Name *name, IR::Expr *target) = 0;
- virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0;
- virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0;
- virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0;
- virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0;
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) = 0;
- virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0;
- virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0;
- virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0;
- virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0;
- virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0;
- virtual void copyValue(IR::Expr *source, IR::Expr *target) = 0;
- virtual void swapValues(IR::Expr *source, IR::Expr *target) = 0;
- virtual void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) = 0;
- virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0;
-
-protected:
- virtual void visitJump(IR::Jump *) = 0;
- virtual void visitCJump(IR::CJump *) = 0;
- virtual void visitRet(IR::Ret *) = 0;
- virtual void visitPhi(IR::Phi *) {}
-
- virtual void callBuiltin(IR::Call *c, IR::Expr *result);
-
- IR::Function *_function; // subclass needs to set
-};
-} // namespace IR
-
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_P_H
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
deleted file mode 100644
index e949e6f0ad..0000000000
--- a/src/qml/compiler/qv4isel_util_p.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_UTIL_P_H
-#define QV4ISEL_UTIL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4value_p.h"
-#include "qv4jsir_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-struct TargetPrimitive32 {
- static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; }
- static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; }
- static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; }
- static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive32 fromDouble(double v) {
- TargetPrimitive32 p;
- memcpy(&p._val, &v, 8);
- return p;
- }
- static TargetPrimitive32 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-struct TargetPrimitive64 {
- static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; }
- static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; }
- static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; }
- static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive64 fromDouble(double v) {
- TargetPrimitive64 p;
- memcpy(&p._val, &v, 8);
- p._val ^= Value::NaNEncodeMask;
- return p;
- }
- static TargetPrimitive64 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-inline bool canConvertToSignedInteger(double value)
-{
- int ival = (int) value;
- // +0 != -0, so we need to convert to double when negating 0
- return ival == value && !(value == 0 && isNegative(value));
-}
-
-inline bool canConvertToUnsignedInteger(double value)
-{
- unsigned uval = (unsigned) value;
- // +0 != -0, so we need to convert to double when negating 0
- return uval == value && !(value == 0 && isNegative(value));
-}
-
-template <typename PrimitiveType = Primitive>
-inline PrimitiveType convertToValue(IR::Const *c)
-{
- switch (c->type) {
- case IR::MissingType:
- return PrimitiveType::emptyValue();
- case IR::NullType:
- return PrimitiveType::nullValue();
- case IR::UndefinedType:
- return PrimitiveType::undefinedValue();
- case IR::BoolType:
- return PrimitiveType::fromBoolean(c->value != 0);
- case IR::SInt32Type:
- return PrimitiveType::fromInt32(int(c->value));
- case IR::UInt32Type:
- return PrimitiveType::fromUInt32(unsigned(c->value));
- case IR::DoubleType:
- return PrimitiveType::fromDouble(c->value);
- case IR::NumberType: {
- int ival = (int)c->value;
- if (canConvertToSignedInteger(c->value)) {
- return PrimitiveType::fromInt32(ival);
- } else {
- return PrimitiveType::fromDouble(c->value);
- }
- }
- default:
- Q_UNREACHABLE();
- }
- // unreachable, but the function must return something
- return PrimitiveType::undefinedValue();
-}
-
-class ConvertTemps
-{
- void renumber(IR::Temp *t)
- {
- if (t->kind != IR::Temp::VirtualRegister)
- return;
-
- int stackSlot = _stackSlotForTemp.value(t->index, -1);
- if (stackSlot == -1) {
- stackSlot = allocateFreeSlot();
- _stackSlotForTemp[t->index] = stackSlot;
- }
-
- t->kind = IR::Temp::StackSlot;
- t->index = stackSlot;
- }
-
-protected:
- int _nextUnusedStackSlot;
- QHash<int, int> _stackSlotForTemp;
- IR::BasicBlock *_currentBasicBlock;
- virtual int allocateFreeSlot()
- {
- return _nextUnusedStackSlot++;
- }
-
- virtual void process(IR::Stmt *s)
- {
- visit(s);
- }
-
-public:
- ConvertTemps()
- : _nextUnusedStackSlot(0)
- , _currentBasicBlock(0)
- {}
-
- void toStackSlots(IR::Function *function)
- {
- _stackSlotForTemp.reserve(function->tempCount);
-
- for (IR::BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _currentBasicBlock = bb;
- for (IR::Stmt *s : bb->statements())
- process(s);
- }
-
- function->tempCount = _nextUnusedStackSlot;
- }
-
-protected:
- void visit(IR::Stmt *s) {
- switch (s->stmtKind) {
- case IR::Stmt::PhiStmt:
- visitPhi(s->asPhi());
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
- }
-
- virtual void visitPhi(IR::Phi *)
- { Q_UNREACHABLE(); }
-
-private:
- void visit(IR::Expr *e) {
- if (auto temp = e->asTemp()) {
- renumber(temp);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_UTIL_P_H
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
deleted file mode 100644
index 0b0ed391fb..0000000000
--- a/src/qml/compiler/qv4jsir.cpp
+++ /dev/null
@@ -1,993 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jsir_p.h"
-#include <private/qqmljsast_p.h>
-
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QtCore/QBuffer>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qset.h>
-#include <cmath>
-
-#include <vector>
-
-#ifdef CONST
-#undef CONST
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace IR {
-
-QString typeName(Type t)
-{
- switch (t) {
- case UnknownType: return QStringLiteral("");
- case MissingType: return QStringLiteral("missing");
- case UndefinedType: return QStringLiteral("undefined");
- case NullType: return QStringLiteral("null");
- case BoolType: return QStringLiteral("bool");
- case UInt32Type: return QStringLiteral("uint32");
- case SInt32Type: return QStringLiteral("int32");
- case DoubleType: return QStringLiteral("double");
- case NumberType: return QStringLiteral("number");
- case StringType: return QStringLiteral("string");
- case VarType: return QStringLiteral("var");
- case QObjectType: return QStringLiteral("qobject");
- default: return QStringLiteral("multiple");
- }
-}
-
-const char *opname(AluOp op)
-{
- switch (op) {
- case OpInvalid: return "?";
-
- case OpIfTrue: return "(bool)";
- case OpNot: return "not";
- case OpUMinus: return "neg";
- case OpUPlus: return "plus";
- case OpCompl: return "invert";
- case OpIncrement: return "incr";
- case OpDecrement: return "decr";
-
- case OpBitAnd: return "bitand";
- case OpBitOr: return "bitor";
- case OpBitXor: return "bitxor";
-
- case OpAdd: return "add";
- case OpSub: return "sub";
- case OpMul: return "mul";
- case OpDiv: return "div";
- case OpMod: return "mod";
-
- case OpLShift: return "shl";
- case OpRShift: return "shr";
- case OpURShift: return "asr";
-
- case OpGt: return "gt";
- case OpLt: return "lt";
- case OpGe: return "ge";
- case OpLe: return "le";
- case OpEqual: return "eq";
- case OpNotEqual: return "ne";
- case OpStrictEqual: return "se";
- case OpStrictNotEqual: return "sne";
-
- case OpInstanceof: return "instanceof";
- case OpIn: return "in";
-
- case OpAnd: return "and";
- case OpOr: return "or";
-
- default: return "?";
-
- } // switch
-}
-
-AluOp binaryOperator(int op)
-{
- switch (static_cast<QSOperator::Op>(op)) {
- case QSOperator::Add: return OpAdd;
- case QSOperator::And: return OpAnd;
- case QSOperator::BitAnd: return OpBitAnd;
- case QSOperator::BitOr: return OpBitOr;
- case QSOperator::BitXor: return OpBitXor;
- case QSOperator::Div: return OpDiv;
- case QSOperator::Equal: return OpEqual;
- case QSOperator::Ge: return OpGe;
- case QSOperator::Gt: return OpGt;
- case QSOperator::Le: return OpLe;
- case QSOperator::LShift: return OpLShift;
- case QSOperator::Lt: return OpLt;
- case QSOperator::Mod: return OpMod;
- case QSOperator::Mul: return OpMul;
- case QSOperator::NotEqual: return OpNotEqual;
- case QSOperator::Or: return OpOr;
- case QSOperator::RShift: return OpRShift;
- case QSOperator::StrictEqual: return OpStrictEqual;
- case QSOperator::StrictNotEqual: return OpStrictNotEqual;
- case QSOperator::Sub: return OpSub;
- case QSOperator::URShift: return OpURShift;
- case QSOperator::InstanceOf: return OpInstanceof;
- case QSOperator::In: return OpIn;
- default: return OpInvalid;
- }
-}
-
-class RemoveSharedExpressions
-{
- CloneExpr clone;
- std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound.
- Expr *uniqueExpr;
-
-public:
- RemoveSharedExpressions(): uniqueExpr(0) {}
-
- void operator()(IR::Function *function)
- {
- subexpressions.clear();
- subexpressions.reserve(function->basicBlockCount() * 8);
-
- for (BasicBlock *block : function->basicBlocks()) {
- if (block->isRemoved())
- continue;
- clone.setBasicBlock(block);
-
- for (Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
-private:
- template <typename Expr_>
- Expr_ *cleanup(Expr_ *expr)
- {
- std::vector<Expr *>::iterator it = std::lower_bound(subexpressions.begin(), subexpressions.end(), expr);
- if (it == subexpressions.end() || *it != expr) {
- subexpressions.insert(it, expr);
- IR::Expr *e = expr;
- qSwap(uniqueExpr, e);
- visit(expr);
- qSwap(uniqueExpr, e);
- return static_cast<Expr_ *>(e);
- }
-
- // the cloned expression is unique by definition
- // so we don't need to add it to `subexpressions'.
- return clone(expr);
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- e->expr = cleanup(e->expr);
- } else if (auto m = s->asMove()) {
- m->target = cleanup(m->target);
- m->source = cleanup(m->source);
- } else if (auto c = s->asCJump()) {
- c->cond = cleanup(c->cond);
- } else if (auto r = s->asRet()) {
- r->expr = cleanup(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- c->expr = cleanup(c->expr);
- } else if (auto u = e->asUnop()) {
- u->expr = cleanup(u->expr);
- } else if (auto b = e->asBinop()) {
- b->left = cleanup(b->left);
- b->right = cleanup(b->right);
- } else if (auto c = e->asCall()) {
- c->base = cleanup(c->base);
- for (IR::ExprList *it = c->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto n = e->asNew()) {
- n->base = cleanup(n->base);
- for (IR::ExprList *it = n->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- s->base = cleanup(s->base);
- s->index = cleanup(s->index);
- } else if (auto m = e->asMember()) {
- m->base = cleanup(m->base);
- }
- }
-};
-
-void Name::initGlobal(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = true;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(Builtin builtin, quint32 line, quint32 column)
-{
- this->id = 0;
- this->builtin = builtin;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-const char *builtin_to_string(Name::Builtin b)
-{
- switch (b) {
- case Name::builtin_invalid:
- return "builtin_invalid";
- case Name::builtin_typeof:
- return "builtin_typeof";
- case Name::builtin_delete:
- return "builtin_delete";
- case Name::builtin_throw:
- return "builtin_throw";
- case Name::builtin_rethrow:
- return "builtin_rethrow";
- case Name::builtin_unwind_exception:
- return "builtin_unwind_exception";
- case Name::builtin_push_catch_scope:
- return "builtin_push_catch_scope";
- case IR::Name::builtin_foreach_iterator_object:
- return "builtin_foreach_iterator_object";
- case IR::Name::builtin_foreach_next_property_name:
- return "builtin_foreach_next_property_name";
- case IR::Name::builtin_push_with_scope:
- return "builtin_push_with_scope";
- case IR::Name::builtin_pop_scope:
- return "builtin_pop_scope";
- case IR::Name::builtin_declare_vars:
- return "builtin_declare_vars";
- case IR::Name::builtin_define_array:
- return "builtin_define_array";
- case IR::Name::builtin_define_object_literal:
- return "builtin_define_object_literal";
- case IR::Name::builtin_setup_argument_object:
- return "builtin_setup_argument_object";
- case IR::Name::builtin_convert_this_to_object:
- return "builtin_convert_this_to_object";
- case IR::Name::builtin_qml_context:
- return "builtin_qml_context";
- case IR::Name::builtin_qml_imported_scripts_object:
- return "builtin_qml_imported_scripts_object";
- }
- return "builtin_(###FIXME)";
-};
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{
- if (t1.kind < t2.kind) return true;
- if (t1.kind > t2.kind) return false;
- return t1.index < t2.index;
-}
-
-Function *Module::newFunction(const QString &name, Function *outer)
-{
- Function *f = new Function(this, outer, name);
- functions.append(f);
- if (!outer) {
- if (!isQmlModule) {
- Q_ASSERT(!rootFunction);
- rootFunction = f;
- }
- } else {
- outer->nestedFunctions.append(f);
- }
- return f;
-}
-
-Module::~Module()
-{
- qDeleteAll(functions);
-}
-
-void Module::setFileName(const QString &name)
-{
- fileName = name;
-}
-
-Function::Function(Module *module, Function *outer, const QString &name)
- : module(module)
- , pool(&module->pool)
- , tempCount(0)
- , maxNumberOfArguments(0)
- , outer(outer)
- , insideWithOrCatch(0)
- , hasDirectEval(false)
- , usesArgumentsObject(false)
- , usesThis(false)
- , isStrict(false)
- , isNamedExpression(false)
- , hasTry(false)
- , hasWith(false)
- , isQmlBinding(false)
- , unused(0)
- , line(0)
- , column(0)
- , _allBasicBlocks(0)
- , _statementCount(0)
-{
- this->name = newString(name);
- _basicBlocks.reserve(8);
-}
-
-Function::~Function()
-{
- if (_allBasicBlocks) {
- qDeleteAll(*_allBasicBlocks);
- delete _allBasicBlocks;
- } else {
- qDeleteAll(_basicBlocks);
- }
-
- pool = 0;
- module = 0;
-}
-
-
-const QString *Function::newString(const QString &text)
-{
- return &*strings.insert(text);
-}
-
-BasicBlock *Function::newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode)
-{
- BasicBlock *block = new BasicBlock(this, catchBlock);
- return mode == InsertBlock ? addBasicBlock(block) : block;
-}
-
-BasicBlock *Function::addBasicBlock(BasicBlock *block)
-{
- Q_ASSERT(block->index() < 0);
- block->setIndex(_basicBlocks.size());
- _basicBlocks.append(block);
- return block;
-}
-
-void Function::removeBasicBlock(BasicBlock *block)
-{
- block->markAsRemoved();
- block->in.clear();
- block->out.clear();
-}
-
-int Function::liveBasicBlocksCount() const
-{
- int count = 0;
- for (BasicBlock *bb : basicBlocks())
- if (!bb->isRemoved())
- ++count;
- return count;
-}
-
-void Function::removeSharedExpressions()
-{
- RemoveSharedExpressions removeSharedExpressions;
- removeSharedExpressions(this);
-}
-
-int Function::indexOfArgument(const QStringRef &string) const
-{
- for (int i = formals.size() - 1; i >= 0; --i) {
- if (*formals.at(i) == string)
- return i;
- }
- return -1;
-}
-
-void Function::setScheduledBlocks(const QVector<BasicBlock *> &scheduled)
-{
- Q_ASSERT(!_allBasicBlocks);
- _allBasicBlocks = new QVector<BasicBlock *>(basicBlocks());
- _basicBlocks = scheduled;
- for (int i = 0, ei = basicBlockCount(); i != ei; ++i)
- basicBlock(i)->changeIndex(i);
-}
-
-BasicBlock *Function::getOrCreateBasicBlock(int index)
-{
- if (_basicBlocks.size() <= index) {
- const int oldSize = _basicBlocks.size();
- _basicBlocks.resize(index + 1);
- for (int i = oldSize; i <= index; ++i) {
- BasicBlock *block = new BasicBlock(this, 0);
- block->setIndex(i);
- _basicBlocks[i] = block;
- }
- }
-
- return _basicBlocks.at(index);
-}
-
-void Function::setStatementCount(int cnt)
-{
- _statementCount = cnt;
-}
-
-void BasicBlock::setStatements(const QVector<Stmt *> &newStatements)
-{
- Q_ASSERT(!isRemoved());
- Q_ASSERT(newStatements.size() >= _statements.size());
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- if (!newStatements.contains(p)) {
- // phi-node was not copied over, so:
- p->destroyData();
- }
- } else {
- break;
- }
- }
- _statements = newStatements;
-}
-
-CloneExpr::CloneExpr(BasicBlock *block)
- : block(block), cloned(0)
-{
-}
-
-void CloneExpr::setBasicBlock(BasicBlock *block)
-{
- this->block = block;
-}
-
-ExprList *CloneExpr::clone(ExprList *list)
-{
- if (! list)
- return 0;
-
- ExprList *clonedList = block->function->New<IR::ExprList>();
- clonedList->init(clone(list->expr), clone(list->next));
- return clonedList;
-}
-
-void CloneExpr::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- cloned = cloneConst(c, block->function);
- } else if (auto s = e->asString()) {
- cloned = block->STRING(s->value);
- } else if (auto r = e->asRegExp()) {
- cloned = block->REGEXP(r->value, r->flags);
- } else if (auto n = e->asName()) {
- cloned = cloneName(n, block->function);
- } else if (auto t = e->asTemp()) {
- cloned = cloneTemp(t, block->function);
- } else if (auto a = e->asArgLocal()) {
- cloned = cloneArgLocal(a, block->function);
- } else if (auto c = e->asClosure()) {
- cloned = block->CLOSURE(c->value);
- } else if (auto c = e->asConvert()) {
- cloned = block->CONVERT(clone(c->expr), c->type);
- } else if (auto u = e->asUnop()) {
- cloned = block->UNOP(u->op, clone(u->expr));
- } else if (auto b = e->asBinop()) {
- cloned = block->BINOP(b->op, clone(b->left), clone(b->right));
- } else if (auto c = e->asCall()) {
- cloned = block->CALL(clone(c->base), clone(c->args));
- } else if (auto n = e->asNew()) {
- cloned = block->NEW(clone(n->base), clone(n->args));
- } else if (auto s = e->asSubscript()) {
- cloned = block->SUBSCRIPT(clone(s->base), clone(s->index));
- } else if (auto m = e->asMember()) {
- cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-IRPrinter::IRPrinter(QTextStream *out)
- : out(out)
- , positionSize(Stmt::InvalidId)
- , currentBB(0)
-{
-}
-
-IRPrinter::~IRPrinter()
-{
-}
-
-void IRPrinter::print(Stmt *s)
-{
- visit(s);
-}
-
-void IRPrinter::print(const Expr &e)
-{
- visit(const_cast<Expr *>(&e));
-}
-
-void IRPrinter::print(Expr *e)
-{
- visit(e);
-}
-
-void IRPrinter::print(Function *f)
-{
- if (positionSize == Stmt::InvalidId)
- positionSize = QString::number(f->statementCount()).size();
-
- QString n = f->name ? *f->name : QString();
- if (n.isEmpty())
- n.sprintf("%p", f);
- *out << "function " << n << '(';
-
- for (int i = 0; i < f->formals.size(); ++i) {
- if (i != 0)
- *out << ", ";
- *out << *f->formals.at(i);
- }
- *out << ')' << endl
- << '{' << endl;
-
- for (const QString *local : qAsConst(f->locals))
- *out << " local var " << *local << endl;
-
- bool needsSeperator = !f->locals.isEmpty();
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- if (needsSeperator)
- *out << endl;
- else
- needsSeperator = true;
- print(bb);
- }
- *out << '}' << endl;
-}
-
-void IRPrinter::print(BasicBlock *bb)
-{
- std::swap(currentBB, bb);
- printBlockStart();
-
- for (Stmt *s : currentBB->statements()) {
- if (!s)
- continue;
-
- QByteArray str;
- QBuffer buf(&str);
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- QTextStream *prevOut = &os;
- std::swap(out, prevOut);
- addStmtNr(s);
- visit(s);
- if (s->location.startLine) {
- out->flush();
- for (int i = 58 - str.length(); i > 0; --i)
- *out << ' ';
- *out << " ; line: " << s->location.startLine << ", column: " << s->location.startColumn;
- }
-
- out->flush();
- std::swap(out, prevOut);
-
- *out << " " << str << endl;
- }
-
- std::swap(currentBB, bb);
-}
-
-void IRPrinter::visit(Stmt *s)
-{
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitExp(Exp *s)
-{
- *out << "void ";
- visit(s->expr);
-}
-
-void IRPrinter::visitMove(Move *s)
-{
- if (Temp *targetTemp = s->target->asTemp())
- if (!s->swap && targetTemp->type != UnknownType)
- *out << typeName(targetTemp->type) << ' ';
-
- visit(s->target);
- *out << ' ';
- if (s->swap)
- *out << "<=> ";
- else
- *out << "= ";
- visit(s->source);
-}
-
-void IRPrinter::visitJump(Jump *s)
-{
- *out << "goto L" << s->target->index();
-}
-
-void IRPrinter::visitCJump(CJump *s)
-{
- *out << "if ";
- visit(s->cond);
- *out << " goto L" << s->iftrue->index()
- << " else goto L" << s->iffalse->index();
-}
-
-void IRPrinter::visitRet(Ret *s)
-{
- *out << "return";
- if (s->expr) {
- *out << ' ';
- visit(s->expr);
- }
-}
-
-void IRPrinter::visitPhi(Phi *s)
-{
- if (s->targetTemp->type != UnknownType)
- *out << typeName(s->targetTemp->type) << ' ';
-
- visit(s->targetTemp);
- *out << " = phi ";
- for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
- if (i > 0)
- *out << ", ";
- if (currentBB)
- *out << 'L' << currentBB->in.at(i)->index() << ": ";
- if (s->incoming[i])
- visit(s->incoming[i]);
- }
-}
-
-void IRPrinter::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitConst(Const *e)
-{
- switch (e->type) {
- case QV4::IR::UndefinedType:
- *out << "undefined";
- break;
- case QV4::IR::NullType:
- *out << "null";
- break;
- case QV4::IR::BoolType:
- *out << (e->value ? "true" : "false");
- break;
- case QV4::IR::MissingType:
- *out << "missing";
- break;
- default:
- if (int(e->value) == 0 && int(e->value) == e->value) {
- if (isNegative(e->value))
- *out << "-0";
- else
- *out << "0";
- } else {
- *out << QString::number(e->value, 'g', 16);
- }
- break;
- }
-}
-
-void IRPrinter::visitString(String *e)
-{
- *out << '"' << escape(*e->value) << '"';
-}
-
-void IRPrinter::visitRegExp(RegExp *e)
-{
- char f[3];
- int i = 0;
- if (e->flags & RegExp::RegExp_Global)
- f[i++] = 'g';
- if (e->flags & RegExp::RegExp_IgnoreCase)
- f[i++] = 'i';
- if (e->flags & RegExp::RegExp_Multiline)
- f[i++] = 'm';
- f[i] = 0;
-
- *out << '/' << *e->value << '/' << f;
-}
-
-void IRPrinter::visitName(Name *e)
-{
- if (e->id) {
- if (*e->id != QLatin1String("this"))
- *out << '.';
- *out << *e->id;
- } else {
- *out << builtin_to_string(e->builtin);
- }
-}
-
-void IRPrinter::visitTemp(Temp *e)
-{
- switch (e->kind) {
- case Temp::VirtualRegister: *out << '%' << e->index; break;
- case Temp::PhysicalRegister: *out << (e->type == DoubleType ? "fp" : "r")
- << e->index; break;
- case Temp::StackSlot: *out << '&' << e->index; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitArgLocal(ArgLocal *e)
-{
- switch (e->kind) {
- case ArgLocal::Formal: *out << '#' << e->index; break;
- case ArgLocal::ScopedFormal: *out << '#' << e->index
- << '@' << e->scope; break;
- case ArgLocal::Local: *out << '$' << e->index; break;
- case ArgLocal::ScopedLocal: *out << '$' << e->index
- << '@' << e->scope; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitClosure(Closure *e)
-{
- QString name = e->functionName ? *e->functionName : QString();
- if (name.isEmpty())
- name.sprintf("%x", e->value);
- *out << "closure " << name;
-}
-
-void IRPrinter::visitConvert(Convert *e)
-{
- *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitUnop(Unop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitBinop(Binop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->left);
- *out << ", ";
- visit(e->right);
-}
-
-void IRPrinter::visitCall(Call *e)
-{
- *out << "call ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitNew(New *e)
-{
- *out << "new ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitSubscript(Subscript *e)
-{
- visit(e->base);
- *out << '[';
- visit(e->index);
- *out << ']';
-}
-
-void IRPrinter::visitMember(Member *e)
-{
- if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray
- && e->attachedPropertiesId != 0 && !e->base->asTemp())
- *out << "[[attached property from " << e->attachedPropertiesId << "]]";
- else
- visit(e->base);
- *out << '.' << *e->name;
-#ifndef V4_BOOTSTRAP
- if (e->property)
- *out << " (meta-property " << e->property->coreIndex()
- << " <" << QMetaType::typeName(e->property->propType())
- << ">)";
- else if (e->kind == Member::MemberOfIdObjectsArray)
- *out << "(id object " << e->idIndex << ")";
-#endif
-}
-
-QString IRPrinter::escape(const QString &s)
-{
- QString r;
- for (int i = 0; i < s.length(); ++i) {
- const QChar ch = s.at(i);
- if (ch == QLatin1Char('\n'))
- r += QLatin1String("\\n");
- else if (ch == QLatin1Char('\r'))
- r += QLatin1String("\\r");
- else if (ch == QLatin1Char('\\'))
- r += QLatin1String("\\\\");
- else if (ch == QLatin1Char('"'))
- r += QLatin1String("\\\"");
- else if (ch == QLatin1Char('\''))
- r += QLatin1String("\\'");
- else
- r += ch;
- }
- return r;
-}
-
-void IRPrinter::addStmtNr(Stmt *s)
-{
- if (s->id() >= 0)
- addJustifiedNr(s->id());
-}
-
-void IRPrinter::addJustifiedNr(int pos)
-{
- if (positionSize == Stmt::InvalidId) {
- *out << pos << ": ";
- } else {
- QString posStr;
- if (pos != Stmt::InvalidId)
- posStr = QString::number(pos);
- *out << posStr.rightJustified(positionSize);
- if (pos == Stmt::InvalidId)
- *out << " ";
- else
- *out << ": ";
- }
-}
-
-void IRPrinter::printBlockStart()
-{
- if (currentBB->isRemoved()) {
- *out << "(block has been removed)";
- return;
- }
-
- QByteArray str;
- str.append('L');
- str.append(QByteArray::number(currentBB->index()));
- str.append(':');
- if (currentBB->catchBlock) {
- str.append(" (exception handler L");
- str.append(QByteArray::number(currentBB->catchBlock->index()));
- str.append(')');
- }
- for (int i = 66 - str.length(); i; --i)
- str.append(' ');
- *out << str;
-
- *out << "; predecessors:";
- for (BasicBlock *in : qAsConst(currentBB->in))
- *out << " L" << in->index();
- if (currentBB->in.isEmpty())
- *out << " none";
- if (BasicBlock *container = currentBB->containingGroup())
- *out << ", container: L" << container->index();
- if (currentBB->isGroupStart())
- *out << ", loop_header: yes";
- *out << endl;
-}
-
-} // end of namespace IR
-} // end of namespace QV4
-
-QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
deleted file mode 100644
index 7bdad31260..0000000000
--- a/src/qml/compiler/qv4jsir_p.h
+++ /dev/null
@@ -1,1813 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4JSIR_P_H
-#define QV4JSIR_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include <private/qqmljsmemorypool_p.h>
-#include <private/qqmljsastfwd_p.h>
-#include <private/qflagpointer_p.h>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlmetatype_p.h>
-#endif
-
-#include <QtCore/private/qnumeric_p.h>
-#include <QtCore/QVector>
-#include <QtCore/QString>
-#include <QtCore/QBitArray>
-#include <QtCore/qurl.h>
-#include <QtCore/QVarLengthArray>
-#include <QtCore/QDateTime>
-#include <qglobal.h>
-
-#if defined(CONST) && defined(Q_OS_WIN)
-# define QT_POP_CONST
-# pragma push_macro("CONST")
-# undef CONST // CONST conflicts with our own identifier
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QTextStream;
-class QQmlType;
-class QQmlPropertyData;
-class QQmlPropertyCache;
-class QQmlEnginePrivate;
-struct QQmlImportRef;
-class QQmlTypeNameCache;
-
-namespace QV4 {
-
-inline bool isNegative(double d)
-{
- uchar *dch = (uchar *)&d;
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- return (dch[0] & 0x80);
- else
- return (dch[7] & 0x80);
-
-}
-
-namespace IR {
-
-struct BasicBlock;
-struct Function;
-struct Module;
-
-struct Stmt;
-struct Expr;
-
-// expressions
-struct Const;
-struct String;
-struct RegExp;
-struct Name;
-struct Temp;
-struct ArgLocal;
-struct Closure;
-struct Convert;
-struct Unop;
-struct Binop;
-struct Call;
-struct New;
-struct Subscript;
-struct Member;
-
-// statements
-struct Exp;
-struct Move;
-struct Jump;
-struct CJump;
-struct Ret;
-struct Phi;
-
-template<class T, int Prealloc>
-class VarLengthArray: public QVarLengthArray<T, Prealloc>
-{
-public:
- bool removeOne(const T &element)
- {
- for (int i = 0; i < this->size(); ++i) {
- if (this->at(i) == element) {
- this->remove(i);
- return true;
- }
- }
-
- return false;
- }
-};
-
-// Flag pointer:
-// * The first flag indicates whether the meta object is final.
-// If final, then none of its properties themselves need to
-// be final when considering for lookups in QML.
-// * The second flag indicates whether enums should be included
-// in the lookup of properties or not. The default is false.
-typedef QFlagPointer<QQmlPropertyCache> IRMetaObject;
-
-enum AluOp {
- OpInvalid = 0,
-
- OpIfTrue,
- OpNot,
- OpUMinus,
- OpUPlus,
- OpCompl,
- OpIncrement,
- OpDecrement,
-
- OpBitAnd,
- OpBitOr,
- OpBitXor,
-
- OpAdd,
- OpSub,
- OpMul,
- OpDiv,
- OpMod,
-
- OpLShift,
- OpRShift,
- OpURShift,
-
- OpGt,
- OpLt,
- OpGe,
- OpLe,
- OpEqual,
- OpNotEqual,
- OpStrictEqual,
- OpStrictNotEqual,
-
- OpInstanceof,
- OpIn,
-
- OpAnd,
- OpOr,
-
- LastAluOp = OpOr
-};
-AluOp binaryOperator(int op);
-const char *opname(IR::AluOp op);
-
-enum Type : quint16 {
- UnknownType = 0,
-
- MissingType = 1 << 0,
- UndefinedType = 1 << 1,
- NullType = 1 << 2,
- BoolType = 1 << 3,
-
- SInt32Type = 1 << 4,
- UInt32Type = 1 << 5,
- DoubleType = 1 << 6,
- NumberType = SInt32Type | UInt32Type | DoubleType,
-
- StringType = 1 << 7,
- QObjectType = 1 << 8,
- VarType = 1 << 9
-};
-
-inline bool strictlyEqualTypes(Type t1, Type t2)
-{
- return t1 == t2 || ((t1 & NumberType) && (t2 & NumberType));
-}
-
-QString typeName(Type t);
-
-struct MemberExpressionResolver;
-
-struct DiscoveredType {
- int type;
- MemberExpressionResolver *memberResolver;
-
- DiscoveredType() : type(UnknownType), memberResolver(0) {}
- DiscoveredType(Type t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(int t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(MemberExpressionResolver *memberResolver)
- : type(QObjectType)
- , memberResolver(memberResolver)
- { Q_ASSERT(memberResolver); }
-
- bool test(Type t) const { return type & t; }
- bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); }
-
- bool operator!=(Type other) const { return type != other; }
- bool operator==(Type other) const { return type == other; }
- bool operator==(const DiscoveredType &other) const { return type == other.type; }
- bool operator!=(const DiscoveredType &other) const { return type != other.type; }
-};
-
-struct MemberExpressionResolver
-{
- typedef DiscoveredType (*ResolveFunction)(QQmlEnginePrivate *engine,
- const MemberExpressionResolver *resolver,
- Member *member);
-
- MemberExpressionResolver()
- : resolveMember(0), import(nullptr), propertyCache(nullptr), typenameCache(nullptr), owner(nullptr), flags(0) {}
-
- bool isValid() const { return !!resolveMember; }
- void clear() { *this = MemberExpressionResolver(); }
-
- ResolveFunction resolveMember;
-#ifndef V4_BOOTSTRAP
- QQmlType qmlType;
-#endif
- const QQmlImportRef *import;
- QQmlPropertyCache *propertyCache;
- QQmlTypeNameCache *typenameCache;
- Function *owner;
- unsigned int flags;
-};
-
-struct Q_AUTOTEST_EXPORT Expr {
- enum ExprKind : quint8 {
- NameExpr,
- TempExpr,
- ArgLocalExpr,
- SubscriptExpr,
- MemberExpr,
-
- LastLValue = MemberExpr,
-
- ConstExpr,
- StringExpr,
- RegExpExpr,
- ClosureExpr,
- ConvertExpr,
- UnopExpr,
- BinopExpr,
- CallExpr,
- NewExpr
- };
-
- Type type;
- const ExprKind exprKind;
-
- Expr &operator=(const Expr &other) {
- Q_ASSERT(exprKind == other.exprKind);
- type = other.type;
- return *this;
- }
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>()) {
- return static_cast<To *>(this);
- } else {
- return nullptr;
- }
- }
-
- template <typename To>
- inline const To *as() const {
- if (isa<To>()) {
- return static_cast<const To *>(this);
- } else {
- return nullptr;
- }
- }
-
- Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {}
- bool isLValue() const;
-
- Const *asConst();
- String *asString();
- RegExp *asRegExp();
- Name *asName();
- Temp *asTemp();
- ArgLocal *asArgLocal();
- Closure *asClosure();
- Convert *asConvert();
- Unop *asUnop();
- Binop *asBinop();
- Call *asCall();
- New *asNew();
- Subscript *asSubscript();
- Member *asMember();
-};
-
-#define EXPR_VISIT_ALL_KINDS(e) \
- switch (e->exprKind) { \
- case QV4::IR::Expr::ConstExpr: \
- break; \
- case QV4::IR::Expr::StringExpr: \
- break; \
- case QV4::IR::Expr::RegExpExpr: \
- break; \
- case QV4::IR::Expr::NameExpr: \
- break; \
- case QV4::IR::Expr::TempExpr: \
- break; \
- case QV4::IR::Expr::ArgLocalExpr: \
- break; \
- case QV4::IR::Expr::ClosureExpr: \
- break; \
- case QV4::IR::Expr::ConvertExpr: { \
- auto casted = e->asConvert(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::UnopExpr: { \
- auto casted = e->asUnop(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::BinopExpr: { \
- auto casted = e->asBinop(); \
- visit(casted->left); \
- visit(casted->right); \
- } break; \
- case QV4::IR::Expr::CallExpr: { \
- auto casted = e->asCall(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::NewExpr: { \
- auto casted = e->asNew(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::SubscriptExpr: { \
- auto casted = e->asSubscript(); \
- visit(casted->base); \
- visit(casted->index); \
- } break; \
- case QV4::IR::Expr::MemberExpr: { \
- auto casted = e->asMember(); \
- visit(casted->base); \
- } break; \
- }
-
-struct ExprList {
- Expr *expr;
- ExprList *next;
-
- ExprList(): expr(0), next(0) {}
-
- void init(Expr *expr, ExprList *next = 0)
- {
- this->expr = expr;
- this->next = next;
- }
-};
-
-struct Const: Expr {
- double value;
-
- Const(): Expr(ConstExpr) {}
-
- void init(Type type, double value)
- {
- this->type = type;
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConstExpr; }
-};
-
-struct String: Expr {
- const QString *value;
-
- String(): Expr(StringExpr) {}
-
- void init(const QString *value)
- {
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
-};
-
-struct RegExp: Expr {
- // needs to be compatible with the flags in the lexer, and in RegExpObject
- enum Flags {
- RegExp_Global = 0x01,
- RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
- };
-
- const QString *value;
- int flags;
-
- RegExp(): Expr(RegExpExpr) {}
-
- void init(const QString *value, int flags)
- {
- this->value = value;
- this->flags = flags;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
-};
-
-struct Name: Expr {
- enum Builtin {
- builtin_invalid,
- builtin_typeof,
- builtin_delete,
- builtin_throw,
- builtin_rethrow,
- builtin_unwind_exception,
- builtin_push_catch_scope,
- builtin_foreach_iterator_object,
- builtin_foreach_next_property_name,
- builtin_push_with_scope,
- builtin_pop_scope,
- builtin_declare_vars,
- builtin_define_array,
- builtin_define_object_literal,
- builtin_setup_argument_object,
- builtin_convert_this_to_object,
- builtin_qml_context,
- builtin_qml_imported_scripts_object
- };
-
- const QString *id;
- Builtin builtin;
- bool global : 1;
- bool qmlSingleton : 1;
- bool freeOfSideEffects : 1;
- quint32 line;
- quint32 column;
-
- Name(): Expr(NameExpr) {}
-
- void initGlobal(const QString *id, quint32 line, quint32 column);
- void init(const QString *id, quint32 line, quint32 column);
- void init(Builtin builtin, quint32 line, quint32 column);
-
- static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
-};
-
-struct Q_AUTOTEST_EXPORT Temp: Expr {
- enum Kind {
- Invalid = 0,
- VirtualRegister,
- PhysicalRegister,
- StackSlot
- };
-
- unsigned index : 28;
- unsigned isReadOnly : 1;
- unsigned kind : 3;
-
- // Used when temp is used as base in member expression
- MemberExpressionResolver *memberResolver;
-
- Temp()
- : Expr(TempExpr)
- , index((1 << 28) - 1)
- , isReadOnly(0)
- , kind(Invalid)
- , memberResolver(0)
- {}
-
- Temp(Type type, Kind kind, unsigned index)
- : Expr(TempExpr)
- , index(index)
- , isReadOnly(0)
- , kind(kind)
- , memberResolver(0)
- {
- this->type = type;
- }
-
- void init(unsigned kind, unsigned index)
- {
- this->index = index;
- this->isReadOnly = false;
- this->kind = kind;
- }
-
- bool isInvalid() const { return kind == Invalid; }
-
- static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
-};
-
-inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return t1.index == t2.index && t1.kind == t2.kind && t1.type == t2.type; }
-
-inline bool operator!=(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW
-{ return t.index ^ t.kind ^ seed; }
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW;
-
-struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
- enum Kind {
- Formal = 0,
- ScopedFormal,
- Local,
- ScopedLocal
- };
-
- unsigned index;
- unsigned scope : 29; // how many scopes outside the current one?
- unsigned kind : 2;
- unsigned isArgumentsOrEval : 1;
-
- void init(unsigned kind, unsigned index, unsigned scope)
- {
- Q_ASSERT((kind == ScopedLocal && scope != 0) ||
- (kind == ScopedFormal && scope != 0) ||
- (scope == 0));
-
- this->kind = kind;
- this->index = index;
- this->scope = scope;
- this->isArgumentsOrEval = false;
- }
-
- ArgLocal(): Expr(ArgLocalExpr) {}
-
- bool operator==(const ArgLocal &other) const
- { return index == other.index && scope == other.scope && kind == other.kind; }
-
- static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; }
-};
-
-struct Closure: Expr {
- int value; // index in _module->functions
- const QString *functionName;
-
- Closure(): Expr(ClosureExpr) {}
-
- void init(int functionInModule, const QString *functionName)
- {
- this->value = functionInModule;
- this->functionName = functionName;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; }
-};
-
-struct Convert: Expr {
- Expr *expr;
-
- Convert(): Expr(ConvertExpr) {}
-
- void init(Expr *expr, Type type)
- {
- this->expr = expr;
- this->type = type;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
-};
-
-struct Unop: Expr {
- Expr *expr;
- AluOp op;
-
- Unop(): Expr(UnopExpr) {}
-
- void init(AluOp op, Expr *expr)
- {
- this->op = op;
- this->expr = expr;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
-};
-
-struct Binop: Expr {
- Expr *left; // Temp or Const
- Expr *right; // Temp or Const
- AluOp op;
-
- Binop(): Expr(BinopExpr) {}
-
- void init(AluOp op, Expr *left, Expr *right)
- {
- this->op = op;
- this->left = left;
- this->right = right;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == BinopExpr; }
-};
-
-struct Call: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- Call(): Expr(CallExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == CallExpr; }
-};
-
-struct New: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- New(): Expr(NewExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == NewExpr; }
-};
-
-struct Subscript: Expr {
- Expr *base;
- Expr *index;
-
- Subscript(): Expr(SubscriptExpr) {}
-
- void init(Expr *base, Expr *index)
- {
- this->base = base;
- this->index = index;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
-};
-
-struct Member: Expr {
- // Used for property dependency tracking
- enum MemberKind {
- UnspecifiedMember,
- MemberOfEnum,
- MemberOfQmlScopeObject,
- MemberOfQmlContextObject,
- MemberOfIdObjectsArray,
- MemberOfSingletonObject,
- };
-
- Expr *base;
- const QString *name;
- QQmlPropertyData *property;
- union { // depending on kind
- int attachedPropertiesId;
- int enumValue;
- int idIndex;
- };
- uchar freeOfSideEffects : 1;
-
- // This is set for example for for QObject properties. All sorts of extra behavior
- // is defined when writing to them, for example resettable properties are reset
- // when writing undefined to them, and an exception is thrown when they're missing
- // a reset function. And then there's also Qt.binding().
- uchar inhibitTypeConversionOnWrite: 1;
-
- uchar kind: 3; // MemberKind
-
- Member(): Expr(MemberExpr) {}
-
- void setEnumValue(int value) {
- kind = MemberOfEnum;
- enumValue = value;
- }
-
- void setAttachedPropertiesId(int id) {
- Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray);
- attachedPropertiesId = id;
- }
-
- void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0)
- {
- this->base = base;
- this->name = name;
- this->property = property;
- this->idIndex = index;
- this->freeOfSideEffects = false;
- this->inhibitTypeConversionOnWrite = property != 0;
- this->kind = kind;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == MemberExpr; }
-};
-
-inline bool Expr::isLValue() const {
- if (auto t = as<Temp>())
- return !t->isReadOnly;
- return exprKind <= LastLValue;
-}
-
-struct Stmt {
- enum StmtKind: quint8 {
- MoveStmt,
- ExpStmt,
- JumpStmt,
- CJumpStmt,
- RetStmt,
- PhiStmt
- };
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>())
- return static_cast<To *>(this);
- else
- return nullptr;
- }
-
- enum { InvalidId = -1 };
-
- QQmlJS::AST::SourceLocation location;
-
- explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
-
- Stmt *asTerminator();
-
- Exp *asExp();
- Move *asMove();
- Jump *asJump();
- CJump *asCJump();
- Ret *asRet();
- Phi *asPhi();
-
- int id() const { return _id; }
-
-private: // For memory management in BasicBlock
- friend struct BasicBlock;
-
-private:
- friend struct Function;
- int _id;
-
-public:
- const StmtKind stmtKind;
-};
-
-#define STMT_VISIT_ALL_KINDS(s) \
- switch (s->stmtKind) { \
- case QV4::IR::Stmt::MoveStmt: { \
- auto casted = s->asMove(); \
- visit(casted->target); \
- visit(casted->source); \
- } break; \
- case QV4::IR::Stmt::ExpStmt: { \
- auto casted = s->asExp(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::JumpStmt: \
- break; \
- case QV4::IR::Stmt::CJumpStmt: { \
- auto casted = s->asCJump(); \
- visit(casted->cond); \
- } break; \
- case QV4::IR::Stmt::RetStmt: { \
- auto casted = s->asRet(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::PhiStmt: { \
- auto casted = s->asPhi(); \
- visit(casted->targetTemp); \
- for (auto *e : casted->incoming) { \
- visit(e); \
- } \
- } break; \
- }
-
-struct Exp: Stmt {
- Expr *expr;
-
- Exp(int id): Stmt(id, ExpStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
-};
-
-struct Move: Stmt {
- Expr *target; // LHS - Temp, Name, Member or Subscript
- Expr *source;
- bool swap;
-
- Move(int id): Stmt(id, MoveStmt) {}
-
- void init(Expr *target, Expr *source)
- {
- this->target = target;
- this->source = source;
- this->swap = false;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
-};
-
-struct Jump: Stmt {
- BasicBlock *target;
-
- Jump(int id): Stmt(id, JumpStmt) {}
-
- void init(BasicBlock *target)
- {
- this->target = target;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
-};
-
-struct CJump: Stmt {
- Expr *cond; // Temp, Binop
- BasicBlock *iftrue;
- BasicBlock *iffalse;
- BasicBlock *parent;
-
- CJump(int id): Stmt(id, CJumpStmt) {}
-
- void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
- {
- this->cond = cond;
- this->iftrue = iftrue;
- this->iffalse = iffalse;
- this->parent = parent;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
-};
-
-struct Ret: Stmt {
- Expr *expr;
-
- Ret(int id): Stmt(id, RetStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; }
-};
-
-// Phi nodes can only occur at the start of a basic block. If there are any, they need to be
-// subsequent to eachother, and the first phi node should be the first statement in the basic-block.
-// A number of loops rely on this behavior, so they don't need to walk through the whole list
-// of instructions in a basic-block (e.g. the calls to destroyData in BasicBlock::~BasicBlock).
-struct Phi: Stmt {
- Temp *targetTemp;
- VarLengthArray<Expr *, 4> incoming;
-
- Phi(int id): Stmt(id, PhiStmt) {}
-
- static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; }
-
- void destroyData()
- { incoming.~VarLengthArray(); }
-};
-
-inline Stmt *Stmt::asTerminator()
-{
- if (auto s = asJump()) {
- return s;
- } else if (auto s = asCJump()) {
- return s;
- } else if (auto s = asRet()) {
- return s;
- } else {
- return nullptr;
- }
-}
-
-struct Q_QML_PRIVATE_EXPORT Module {
- QQmlJS::MemoryPool pool;
- QVector<Function *> functions;
- Function *rootFunction;
- QString fileName;
- QDateTime sourceTimeStamp;
- bool isQmlModule; // implies rootFunction is always 0
- uint unitFlags; // flags merged into CompiledData::Unit::flags
- QString targetABI; // fallback to QSysInfo::buildAbi() if empty
-#if !QT_CONFIG(qml_debug)
- static const bool debugMode = false;
-#else
- bool debugMode;
-#endif
-
- Function *newFunction(const QString &name, Function *outer);
-
- Module(bool debugMode)
- : rootFunction(0)
- , isQmlModule(false)
- , unitFlags(0)
-#if QT_CONFIG(qml_debug)
- , debugMode(debugMode)
- {}
-#else
- { Q_UNUSED(debugMode); }
-#endif
- ~Module();
-
- void setFileName(const QString &name);
-};
-
-struct BasicBlock {
-private:
- Q_DISABLE_COPY(BasicBlock)
-
-public:
- typedef VarLengthArray<BasicBlock *, 4> IncomingEdges;
- typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges;
-
- Function *function;
- BasicBlock *catchBlock;
- IncomingEdges in;
- OutgoingEdges out;
- QQmlJS::AST::SourceLocation nextLocation;
-
- BasicBlock(Function *function, BasicBlock *catcher)
- : function(function)
- , catchBlock(catcher)
- , _containingGroup(0)
- , _index(-1)
- , _isExceptionHandler(false)
- , _groupStart(false)
- , _isRemoved(false)
- {}
-
- ~BasicBlock()
- {
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- p->destroyData();
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements() const
- {
- Q_ASSERT(!isRemoved());
- return _statements;
- }
-
- int statementCount() const
- {
- Q_ASSERT(!isRemoved());
- return _statements.size();
- }
-
- void setStatements(const QVector<Stmt *> &newStatements);
-
- template <typename Instr> inline Instr i(Instr i)
- {
- Q_ASSERT(!isRemoved());
- appendStatement(i);
- return i;
- }
-
- void appendStatement(Stmt *statement)
- {
- Q_ASSERT(!isRemoved());
- if (nextLocation.startLine)
- statement->location = nextLocation;
- _statements.append(statement);
- }
-
- void prependStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.prepend(stmt);
- }
-
- void prependStatements(const QVector<Stmt *> &stmts)
- {
- Q_ASSERT(!isRemoved());
- QVector<Stmt *> newStmts = stmts;
- newStmts += _statements;
- _statements = newStmts;
- }
-
- void insertStatementBefore(Stmt *before, Stmt *newStmt)
- {
- int idx = _statements.indexOf(before);
- Q_ASSERT(idx >= 0);
- _statements.insert(idx, newStmt);
- }
-
- void insertStatementBefore(int index, Stmt *newStmt)
- {
- Q_ASSERT(index >= 0);
- _statements.insert(index, newStmt);
- }
-
- void insertStatementBeforeTerminator(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.insert(_statements.size() - 1, stmt);
- }
-
- void replaceStatement(int index, Stmt *newStmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[index]->asPhi()) {
- p->destroyData();
- }
- _statements[index] = newStmt;
- }
-
- void removeStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = stmt->asPhi()) {
- p->destroyData();
- }
- _statements.remove(_statements.indexOf(stmt));
- }
-
- void removeStatement(int idx)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[idx]->asPhi()) {
- p->destroyData();
- }
- _statements.remove(idx);
- }
-
- inline bool isEmpty() const {
- Q_ASSERT(!isRemoved());
- return _statements.isEmpty();
- }
-
- inline Stmt *terminator() const {
- Q_ASSERT(!isRemoved());
- if (! _statements.isEmpty() && _statements.last()->asTerminator() != 0)
- return _statements.last();
- return 0;
- }
-
- inline bool isTerminated() const {
- Q_ASSERT(!isRemoved());
- if (terminator() != 0)
- return true;
- return false;
- }
-
- enum TempForWhom {
- NewTempForCodegen,
- NewTempForOptimizer
- };
- unsigned newTemp(TempForWhom tfw = NewTempForCodegen);
-
- Temp *TEMP(unsigned kind);
- ArgLocal *ARG(unsigned index, unsigned scope);
- ArgLocal *LOCAL(unsigned index, unsigned scope);
-
- Expr *CONST(Type type, double value);
- Expr *STRING(const QString *value);
- Expr *REGEXP(const QString *value, int flags);
-
- Name *NAME(const QString &id, quint32 line, quint32 column);
- Name *NAME(Name::Builtin builtin, quint32 line, quint32 column);
-
- Name *GLOBALNAME(const QString &id, quint32 line, quint32 column);
-
- Closure *CLOSURE(int functionInModule);
-
- Expr *CONVERT(Expr *expr, Type type);
- Expr *UNOP(AluOp op, Expr *expr);
- Expr *BINOP(AluOp op, Expr *left, Expr *right);
- Expr *CALL(Expr *base, ExprList *args = 0);
- Expr *NEW(Expr *base, ExprList *args = 0);
- Expr *SUBSCRIPT(Expr *base, Expr *index);
- Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0);
-
- Stmt *EXP(Expr *expr);
-
- Stmt *MOVE(Expr *target, Expr *source);
-
- Stmt *JUMP(BasicBlock *target);
- Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
- Stmt *RET(Expr *expr);
-
- BasicBlock *containingGroup() const
- {
- Q_ASSERT(!isRemoved());
- return _containingGroup;
- }
-
- void setContainingGroup(BasicBlock *loopHeader)
- {
- Q_ASSERT(!isRemoved());
- _containingGroup = loopHeader;
- }
-
- bool isGroupStart() const
- {
- Q_ASSERT(!isRemoved());
- return _groupStart;
- }
-
- void markAsGroupStart(bool mark = true)
- {
- Q_ASSERT(!isRemoved());
- _groupStart = mark;
- }
-
- // Returns the index of the basic-block.
- // See Function for the full description.
- int index() const
- {
- Q_ASSERT(!isRemoved());
- return _index;
- }
-
- bool isExceptionHandler() const
- { return _isExceptionHandler; }
-
- void setExceptionHandler(bool onoff)
- { _isExceptionHandler = onoff; }
-
- bool isRemoved() const
- { return _isRemoved; }
-
-private: // For Function's eyes only.
- friend struct Function;
- void setIndex(int index)
- {
- Q_ASSERT(_index < 0);
- changeIndex(index);
- }
-
- void changeIndex(int index)
- {
- Q_ASSERT(index >= 0);
- _index = index;
- }
-
- void markAsRemoved()
- {
- _isRemoved = true;
- _index = -1;
- }
-
-private:
- QVector<Stmt *> _statements;
- BasicBlock *_containingGroup;
- int _index;
- unsigned _isExceptionHandler : 1;
- unsigned _groupStart : 1;
- unsigned _isRemoved : 1;
-};
-
-template <typename T>
-class SmallSet: public QVarLengthArray<T, 8>
-{
-public:
- void insert(int value)
- {
- for (auto it : *this) {
- if (it == value)
- return;
- }
- this->append(value);
- }
-};
-
-// Map from meta property index (existence implies dependency) to notify signal index
-struct KeyValuePair
-{
- quint32 _key;
- quint32 _value;
-
- KeyValuePair(): _key(0), _value(0) {}
- KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
-
- quint32 key() const { return _key; }
- quint32 value() const { return _value; }
-};
-
-class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
-{
-public:
- void insert(quint32 key, quint32 value)
- {
- for (auto it = begin(), eit = end(); it != eit; ++it) {
- if (it->_key == key) {
- it->_value = value;
- return;
- }
- }
- append(KeyValuePair(key, value));
- }
-};
-
-// The Function owns (manages), among things, a list of basic-blocks. All the blocks have an index,
-// which corresponds to the index in the entry/index in the vector in which they are stored. This
-// means that algorithms/classes can also store any information about a basic block in an array,
-// where the index corresponds to the index of the basic block, which can then be used to query
-// the function for a pointer to a basic block. This also means that basic-blocks cannot be removed
-// or renumbered.
-//
-// Note that currently there is one exception: after optimization and block scheduling, the
-// method setScheduledBlocks can be called once, to register a newly ordered list. For debugging
-// purposes, these blocks are not immediately renumbered, so renumberBasicBlocks should be called
-// immediately after changing the order. That will restore the property of having a corresponding
-// block-index and block-position-in-basicBlocks-vector.
-//
-// In order for optimization/transformation passes to skip uninteresting basic blocks that will be
-// removed, the block can be marked as such. After doing so, any access will result in a failing
-// assertion.
-struct Function {
- Module *module;
- QQmlJS::MemoryPool *pool;
- const QString *name;
- int currentTemp = 0;
- int tempCount;
- int maxNumberOfArguments;
- QSet<QString> strings;
- QList<const QString *> formals;
- QList<const QString *> locals;
- QVector<Function *> nestedFunctions;
- Function *outer;
-
- int insideWithOrCatch;
-
- uint hasDirectEval: 1;
- uint usesArgumentsObject : 1;
- uint usesThis : 1;
- uint isStrict: 1;
- uint isNamedExpression : 1;
- uint hasTry: 1;
- uint hasWith: 1;
- uint isQmlBinding: 1;
- uint unused : 24;
-
- // Location of declaration in source code (0 if not specified)
- uint line;
- uint column;
-
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
- template <typename T> T *New() { return new (pool->allocate(sizeof(T))) T(); }
- template <typename T> T *NewStmt() {
- return new (pool->allocate(sizeof(T))) T(getNewStatementId());
- }
-
- Function(Module *module, Function *outer, const QString &name);
- ~Function();
-
- enum BasicBlockInsertMode {
- InsertBlock,
- DontInsertBlock
- };
-
- BasicBlock *newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock);
- const QString *newString(const QString &text);
-
- void RECEIVE(const QString &name) { formals.append(newString(name)); }
- void LOCAL(const QString &name) { locals.append(newString(name)); }
-
- BasicBlock *addBasicBlock(BasicBlock *block);
- void removeBasicBlock(BasicBlock *block);
-
- const QVector<BasicBlock *> &basicBlocks() const
- { return _basicBlocks; }
-
- BasicBlock *basicBlock(int idx) const
- { return _basicBlocks.at(idx); }
-
- int basicBlockCount() const
- { return _basicBlocks.size(); }
-
- int liveBasicBlocksCount() const;
-
- void removeSharedExpressions();
-
- int indexOfArgument(const QStringRef &string) const;
-
- bool variablesCanEscape() const
- { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; }
-
- void setScheduledBlocks(const QVector<BasicBlock *> &scheduled);
-
- int getNewStatementId() { return _statementCount++; }
- int statementCount() const { return _statementCount; }
-
- bool canUseSimpleCall() const {
- return nestedFunctions.isEmpty() &&
- locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount &&
- !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval;
- }
-
- bool argLocalRequiresWriteBarrier(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return !f->canUseSimpleCall();
- }
- int localsCountForScope(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return f->locals.size();
- }
-
-private:
- BasicBlock *getOrCreateBasicBlock(int index);
- void setStatementCount(int cnt);
-
-private:
- QVector<BasicBlock *> _basicBlocks;
- QVector<BasicBlock *> *_allBasicBlocks;
- int _statementCount;
-};
-
-class CloneExpr
-{
-public:
- explicit CloneExpr(IR::BasicBlock *block = 0);
-
- void setBasicBlock(IR::BasicBlock *block);
-
- template <typename ExprSubclass>
- ExprSubclass *operator()(ExprSubclass *expr)
- {
- return clone(expr);
- }
-
- template <typename ExprSubclass>
- ExprSubclass *clone(ExprSubclass *expr)
- {
- Expr *c = expr;
- qSwap(cloned, c);
- visit(expr);
- qSwap(cloned, c);
- return static_cast<ExprSubclass *>(c);
- }
-
- static Const *cloneConst(Const *c, Function *f)
- {
- Const *newConst = f->New<Const>();
- newConst->init(c->type, c->value);
- return newConst;
- }
-
- static Name *cloneName(Name *n, Function *f)
- {
- Name *newName = f->New<Name>();
- newName->type = n->type;
- newName->id = n->id;
- newName->builtin = n->builtin;
- newName->global = n->global;
- newName->qmlSingleton = n->qmlSingleton;
- newName->freeOfSideEffects = n->freeOfSideEffects;
- newName->line = n->line;
- newName->column = n->column;
- return newName;
- }
-
- static Temp *cloneTemp(Temp *t, Function *f)
- {
- Temp *newTemp = f->New<Temp>();
- newTemp->init(t->kind, t->index);
- newTemp->type = t->type;
- newTemp->memberResolver = t->memberResolver;
- return newTemp;
- }
-
- static ArgLocal *cloneArgLocal(ArgLocal *argLocal, Function *f)
- {
- ArgLocal *newArgLocal = f->New<ArgLocal>();
- newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope);
- newArgLocal->type = argLocal->type;
- newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval;
- return newArgLocal;
- }
-
-private:
- IR::ExprList *clone(IR::ExprList *list);
-
- void visit(Expr *e);
-
-protected:
- IR::BasicBlock *block;
-
-private:
- IR::Expr *cloned;
-};
-
-class Q_AUTOTEST_EXPORT IRPrinter
-{
-public:
- IRPrinter(QTextStream *out);
- virtual ~IRPrinter();
-
- void print(Stmt *s);
- void print(Expr *e);
- void print(const Expr &e);
-
- virtual void print(Function *f);
- virtual void print(BasicBlock *bb);
-
- void visit(Stmt *s);
- virtual void visitExp(Exp *s);
- virtual void visitMove(Move *s);
- virtual void visitJump(Jump *s);
- virtual void visitCJump(CJump *s);
- virtual void visitRet(Ret *s);
- virtual void visitPhi(Phi *s);
-
- void visit(Expr *e);
- virtual void visitConst(Const *e);
- virtual void visitString(String *e);
- virtual void visitRegExp(RegExp *e);
- virtual void visitName(Name *e);
- virtual void visitTemp(Temp *e);
- virtual void visitArgLocal(ArgLocal *e);
- virtual void visitClosure(Closure *e);
- virtual void visitConvert(Convert *e);
- virtual void visitUnop(Unop *e);
- virtual void visitBinop(Binop *e);
- virtual void visitCall(Call *e);
- virtual void visitNew(New *e);
- virtual void visitSubscript(Subscript *e);
- virtual void visitMember(Member *e);
-
- static QString escape(const QString &s);
-
-protected:
- virtual void addStmtNr(Stmt *s);
- void addJustifiedNr(int pos);
- void printBlockStart();
-
-protected:
- QTextStream *out;
- int positionSize;
- BasicBlock *currentBB;
-};
-
-inline unsigned BasicBlock::newTemp(TempForWhom tfw)
-{
- Q_ASSERT(!isRemoved());
-
- if (tfw == NewTempForOptimizer)
- return function->tempCount++;
-
- int t = function->currentTemp++;
- if (function->tempCount < function->currentTemp)
- function->tempCount = function->currentTemp;
- return t;
-}
-
-inline Temp *BasicBlock::TEMP(unsigned index)
-{
- Q_ASSERT(!isRemoved());
- Temp *e = function->New<Temp>();
- e->init(Temp::VirtualRegister, index);
- return e;
-}
-
-inline ArgLocal *BasicBlock::ARG(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedFormal : ArgLocal::Formal, index, scope);
- return e;
-}
-
-inline ArgLocal *BasicBlock::LOCAL(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedLocal : ArgLocal::Local, index, scope);
- return e;
-}
-
-inline Expr *BasicBlock::CONST(Type type, double value)
-{
- Q_ASSERT(!isRemoved());
- Const *e = function->New<Const>();
- if (type == NumberType) {
- int ival = (int)value;
- // +0 != -0, so we need to convert to double when negating 0
- if (ival == value && !(value == 0 && isNegative(value)))
- type = SInt32Type;
- else
- type = DoubleType;
- } else if (type == NullType) {
- value = 0;
- } else if (type == UndefinedType) {
- value = qt_qnan();
- }
-
- e->init(type, value);
- return e;
-}
-
-inline Expr *BasicBlock::STRING(const QString *value)
-{
- Q_ASSERT(!isRemoved());
- String *e = function->New<String>();
- e->init(value);
- return e;
-}
-
-inline Expr *BasicBlock::REGEXP(const QString *value, int flags)
-{
- Q_ASSERT(!isRemoved());
- RegExp *e = function->New<RegExp>();
- e->init(value, flags);
- return e;
-}
-
-inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(function->newString(id), line, column);
- return e;
-}
-
-inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->initGlobal(function->newString(id), line, column);
- return e;
-}
-
-
-inline Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(builtin, line, column);
- return e;
-}
-
-inline Closure *BasicBlock::CLOSURE(int functionInModule)
-{
- Q_ASSERT(!isRemoved());
- Closure *clos = function->New<Closure>();
- clos->init(functionInModule, function->module->functions.at(functionInModule)->name);
- return clos;
-}
-
-inline Expr *BasicBlock::CONVERT(Expr *expr, Type type)
-{
- Q_ASSERT(!isRemoved());
- Convert *e = function->New<Convert>();
- e->init(expr, type);
- return e;
-}
-
-inline Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- Unop *e = function->New<Unop>();
- e->init(op, expr);
- return e;
-}
-
-inline Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
-{
- Q_ASSERT(!isRemoved());
- Binop *e = function->New<Binop>();
- e->init(op, left, right);
- return e;
-}
-
-inline Expr *BasicBlock::CALL(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- Call *e = function->New<Call>();
- e->init(base, args);
- int argc = 0;
- for (ExprList *it = args; it; it = it->next)
- ++argc;
- function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc);
- return e;
-}
-
-inline Expr *BasicBlock::NEW(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- New *e = function->New<New>();
- e->init(base, args);
- return e;
-}
-
-inline Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
-{
- Q_ASSERT(!isRemoved());
- Subscript *e = function->New<Subscript>();
- e->init(base, index);
- return e;
-}
-
-inline Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue)
-{
- Q_ASSERT(!isRemoved());
- Member*e = function->New<Member>();
- e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue);
- return e;
-}
-
-inline Stmt *BasicBlock::EXP(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Exp *s = function->NewStmt<Exp>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Move *s = function->NewStmt<Move>();
- s->init(target, source);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::JUMP(BasicBlock *target)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Jump *s = function->NewStmt<Jump>();
- s->init(target);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(target));
- out.append(target);
-
- Q_ASSERT(! target->in.contains(this));
- target->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- if (iftrue == iffalse) {
- MOVE(TEMP(newTemp()), cond);
- return JUMP(iftrue);
- }
-
- CJump *s = function->NewStmt<CJump>();
- s->init(cond, iftrue, iffalse, this);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(iftrue));
- out.append(iftrue);
-
- Q_ASSERT(! iftrue->in.contains(this));
- iftrue->in.append(this);
-
- Q_ASSERT(! out.contains(iffalse));
- out.append(iffalse);
-
- Q_ASSERT(! iffalse->in.contains(this));
- iffalse->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::RET(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Ret *s = function->NewStmt<Ret>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Const *Expr::asConst() { return as<Const>(); }
-inline String *Expr::asString() { return as<String>(); }
-inline RegExp *Expr::asRegExp() { return as<RegExp>(); }
-inline Name *Expr::asName() { return as<Name>(); }
-inline Temp *Expr::asTemp() { return as<Temp>(); }
-inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); }
-inline Closure *Expr::asClosure() { return as<Closure>(); }
-inline Convert *Expr::asConvert() { return as<Convert>(); }
-inline Unop *Expr::asUnop() { return as<Unop>(); }
-inline Binop *Expr::asBinop() { return as<Binop>(); }
-inline Call *Expr::asCall() { return as<Call>(); }
-inline New *Expr::asNew() { return as<New>(); }
-inline Subscript *Expr::asSubscript() { return as<Subscript>(); }
-inline Member *Expr::asMember() { return as<Member>(); }
-
-inline Exp *Stmt::asExp() { return as<Exp>(); }
-inline Move *Stmt::asMove() { return as<Move>(); }
-inline Jump *Stmt::asJump() { return as<Jump>(); }
-inline CJump *Stmt::asCJump() { return as<CJump>(); }
-inline Ret *Stmt::asRet() { return as<Ret>(); }
-inline Phi *Stmt::asPhi() { return as<Phi>(); }
-
-} // end of namespace IR
-
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-#if defined(QT_POP_CONST)
-# pragma pop_macro("CONST") // Restore peace
-# undef QT_POP_CONST
-#endif
-
-#endif // QV4IR_P_H
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
deleted file mode 100644
index cf79168c6c..0000000000
--- a/src/qml/compiler/qv4ssa.cpp
+++ /dev/null
@@ -1,5848 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// When building with debug code, the macro below will enable debug helpers when using libc++.
-// For example, the std::vector<T>::operator[] will use _LIBCPP_ASSERT to check if the index is
-// within the array bounds. Note that this only works reliably with OSX 10.9 or later.
-//#define _LIBCPP_DEBUG2 2
-
-#include "qv4ssa_p.h"
-#include "qv4isel_util_p.h"
-#include "qv4util_p.h"
-
-#include <QtCore/QBuffer>
-#include <QtCore/QCoreApplication>
-#include <QtCore/QStringList>
-#include <QtCore/QSet>
-#include <QtCore/QLinkedList>
-#include <QtCore/QStack>
-#include <qv4runtime_p.h>
-#include <cmath>
-#include <iostream>
-#include <cassert>
-
-QT_USE_NAMESPACE
-
-using namespace QV4;
-using namespace IR;
-
-namespace {
-
-enum { DebugMoveMapping = 0 };
-
-#ifdef QT_NO_DEBUG
-enum { DoVerification = 0 };
-#else
-enum { DoVerification = 1 };
-#endif
-
-static void showMeTheCode(IR::Function *function, const char *marker)
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (showCode) {
- qDebug() << marker;
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream stream(&buf);
- IRPrinter(&stream).print(function);
- stream << endl;
- qDebug("%s", buf.data().constData());
- }
-}
-
-class ProcessedBlocks
-{
- BitVector processed;
-
-public:
- ProcessedBlocks(IR::Function *function)
- : processed(function->basicBlockCount(), false)
- {}
-
- bool alreadyProcessed(BasicBlock *bb) const
- {
- Q_ASSERT(bb);
-
- return processed.at(bb->index());
- }
-
- void markAsProcessed(BasicBlock *bb)
- {
- processed.setBit(bb->index());
- }
-};
-
-class BasicBlockSet
-{
- typedef BitVector Flags;
-
- QVarLengthArray<int, 8> blockNumbers;
- Flags *blockFlags;
- IR::Function *function;
- enum { MaxVectorCapacity = 8 };
-
-public:
- class const_iterator
- {
- const BasicBlockSet &set;
- // ### These two members could go into a union, but clang won't compile (https://codereview.qt-project.org/#change,74259)
- QVarLengthArray<int, 8>::const_iterator numberIt;
- int flagIt;
-
- friend class BasicBlockSet;
- const_iterator(const BasicBlockSet &set, bool end)
- : set(set)
- {
- if (end || !set.function) {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.end();
- else
- flagIt = set.blockFlags->size();
- } else {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.begin();
- else
- findNextWithFlags(0);
- }
- }
-
- void findNextWithFlags(int start)
- {
- flagIt = set.blockFlags->findNext(start, true, /*wrapAround = */false);
- Q_ASSERT(flagIt <= set.blockFlags->size());
- }
-
- public:
- BasicBlock *operator*() const
- {
- if (!set.blockFlags) {
- return set.function->basicBlock(*numberIt);
- } else {
- Q_ASSERT(flagIt <= set.function->basicBlockCount());
- return set.function->basicBlock(flagIt);
- }
- }
-
- bool operator==(const const_iterator &other) const
- {
- if (&set != &other.set)
- return false;
- if (!set.blockFlags)
- return numberIt == other.numberIt;
- else
- return flagIt == other.flagIt;
- }
-
- bool operator!=(const const_iterator &other) const
- { return !(*this == other); }
-
- const_iterator &operator++()
- {
- if (!set.blockFlags)
- ++numberIt;
- else
- findNextWithFlags(flagIt + 1);
-
- return *this;
- }
- };
-
- friend class const_iterator;
-
-public:
- BasicBlockSet(IR::Function *f = 0): blockFlags(0), function(0)
- {
- if (f)
- init(f);
- }
-
-#ifdef Q_COMPILER_RVALUE_REFS
- BasicBlockSet(BasicBlockSet &&other): blockFlags(0)
- {
- std::swap(blockNumbers, other.blockNumbers);
- std::swap(blockFlags, other.blockFlags);
- std::swap(function, other.function);
- }
-#endif // Q_COMPILER_RVALUE_REFS
-
- BasicBlockSet(const BasicBlockSet &other)
- : blockFlags(0)
- , function(other.function)
- {
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- }
-
- BasicBlockSet &operator=(const BasicBlockSet &other)
- {
- if (blockFlags) {
- delete blockFlags;
- blockFlags = 0;
- }
- function = other.function;
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- return *this;
- }
-
- ~BasicBlockSet()
- {
- delete blockFlags;
- }
-
- void init(IR::Function *f)
- {
- Q_ASSERT(!function);
- Q_ASSERT(f);
- function = f;
- }
-
- bool empty() const
- {
- return begin() == end();
- }
-
- void insert(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->setBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return;
- }
-
- if (blockNumbers.size() == MaxVectorCapacity) {
- blockFlags = new Flags(function->basicBlockCount(), false);
- for (int i = 0; i < blockNumbers.size(); ++i) {
- blockFlags->setBit(blockNumbers[i]);
- }
- blockNumbers.clear();
- blockFlags->setBit(bb->index());
- } else {
- blockNumbers.append(bb->index());
- }
- }
-
- void remove(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->clearBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index()) {
- blockNumbers.remove(i);
- return;
- }
- }
- }
-
- const_iterator begin() const { return const_iterator(*this, false); }
- const_iterator end() const { return const_iterator(*this, true); }
-
- void collectValues(std::vector<BasicBlock *> &bbs) const
- {
- Q_ASSERT(function);
-
- for (const_iterator it = begin(), eit = end(); it != eit; ++it)
- bbs.push_back(*it);
- }
-
- bool contains(BasicBlock *bb) const
- {
- Q_ASSERT(function);
-
- if (blockFlags)
- return blockFlags->at(bb->index());
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return true;
- }
-
- return false;
- }
-};
-
-class DominatorTree
-{
- enum {
- DebugDominatorFrontiers = 0,
- DebugImmediateDominators = 0,
-
- DebugCodeCanUseLotsOfCpu = 0
- };
-
- typedef int BasicBlockIndex;
- enum { InvalidBasicBlockIndex = -1 };
-
- struct Data
- {
- int N;
- std::vector<int> dfnum; // BasicBlock index -> dfnum
- std::vector<int> vertex;
- std::vector<BasicBlockIndex> parent; // BasicBlock index -> parent BasicBlock index
- std::vector<BasicBlockIndex> ancestor; // BasicBlock index -> ancestor BasicBlock index
- std::vector<BasicBlockIndex> best; // BasicBlock index -> best BasicBlock index
- std::vector<BasicBlockIndex> semi; // BasicBlock index -> semi dominator BasicBlock index
- std::vector<BasicBlockIndex> samedom; // BasicBlock index -> same dominator BasicBlock index
-
- Data(): N(0) {}
- };
-
- IR::Function *function;
- QScopedPointer<Data> d;
- std::vector<BasicBlockIndex> idom; // BasicBlock index -> immediate dominator BasicBlock index
- std::vector<BasicBlockSet> DF; // BasicBlock index -> dominator frontier
-
- struct DFSTodo {
- BasicBlockIndex node, parent;
-
- DFSTodo()
- : node(InvalidBasicBlockIndex)
- , parent(InvalidBasicBlockIndex)
- {}
-
- DFSTodo(BasicBlockIndex node, BasicBlockIndex parent)
- : node(node)
- , parent(parent)
- {}
- };
-
- void DFS(BasicBlockIndex node) {
- std::vector<DFSTodo> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
- DFSTodo todo(node, InvalidBasicBlockIndex);
-
- while (true) {
- BasicBlockIndex n = todo.node;
-
- if (d->dfnum[n] == 0) {
- d->dfnum[n] = d->N;
- d->vertex[d->N] = n;
- d->parent[n] = todo.parent;
- ++d->N;
- const BasicBlock::OutgoingEdges &out = function->basicBlock(n)->out;
- for (int i = out.size() - 1; i > 0; --i)
- worklist.push_back(DFSTodo(out[i]->index(), n));
-
- if (out.size() > 0) {
- todo.node = out.first()->index();
- todo.parent = n;
- continue;
- }
- }
-
- if (worklist.empty())
- break;
-
- todo = worklist.back();
- worklist.pop_back();
- }
- }
-
- BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) {
- worklist.clear();
- for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = d->ancestor[it])
- worklist.push_back(it);
-
- if (worklist.size() < 2)
- return d->best[v];
-
- BasicBlockIndex b = InvalidBasicBlockIndex;
- BasicBlockIndex last = worklist.back();
- Q_ASSERT(worklist.size() <= INT_MAX);
- for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
- BasicBlockIndex bbIt = worklist[it];
- d->ancestor[bbIt] = last;
- BasicBlockIndex &best_it = d->best[bbIt];
- if (b != InvalidBasicBlockIndex && d->dfnum[d->semi[b]] < d->dfnum[d->semi[best_it]])
- best_it = b;
- else
- b = best_it;
- }
- return b;
- }
-
- void link(BasicBlockIndex p, BasicBlockIndex n) {
- d->ancestor[n] = p;
- d->best[n] = n;
- }
-
- void calculateIDoms() {
- Q_ASSERT(function->basicBlock(0)->in.isEmpty());
-
- const int bbCount = function->basicBlockCount();
- d->vertex = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->parent = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->dfnum = std::vector<int>(size_t(bbCount), 0);
- d->semi = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->ancestor = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- idom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->samedom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->best = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
-
- QHash<BasicBlockIndex, std::vector<BasicBlockIndex> > bucket;
- bucket.reserve(bbCount);
-
- DFS(function->basicBlock(0)->index());
- Q_ASSERT(d->N == function->liveBasicBlocksCount());
-
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
-
- for (int i = d->N - 1; i > 0; --i) {
- BasicBlockIndex n = d->vertex[i];
- BasicBlockIndex p = d->parent[n];
- BasicBlockIndex s = p;
-
- for (BasicBlock *v : function->basicBlock(n)->in) {
- BasicBlockIndex ss = InvalidBasicBlockIndex;
- if (d->dfnum[v->index()] <= d->dfnum[n])
- ss = v->index();
- else
- ss = d->semi[ancestorWithLowestSemi(v->index(), worklist)];
- if (d->dfnum[ss] < d->dfnum[s])
- s = ss;
- }
- d->semi[n] = s;
- bucket[s].push_back(n);
- link(p, n);
- if (bucket.contains(p)) {
- for (BasicBlockIndex v : bucket[p]) {
- BasicBlockIndex y = ancestorWithLowestSemi(v, worklist);
- BasicBlockIndex semi_v = d->semi[v];
- if (d->semi[y] == semi_v)
- idom[v] = semi_v;
- else
- d->samedom[v] = y;
- }
- bucket.remove(p);
- }
- }
-
- for (int i = 1; i < d->N; ++i) {
- BasicBlockIndex n = d->vertex[i];
- Q_ASSERT(n != InvalidBasicBlockIndex);
- Q_ASSERT(!bucket.contains(n));
- Q_ASSERT(d->ancestor[n] != InvalidBasicBlockIndex
- && ((d->semi[n] != InvalidBasicBlockIndex
- && d->dfnum[d->ancestor[n]] <= d->dfnum[d->semi[n]]) || d->semi[n] == n));
- BasicBlockIndex sdn = d->samedom[n];
- if (sdn != InvalidBasicBlockIndex)
- idom[n] = idom[sdn];
- }
-
- dumpImmediateDominators();
- }
-
- struct NodeProgress {
- std::vector<BasicBlockIndex> children;
- std::vector<BasicBlockIndex> todo;
- };
-
-public:
- DominatorTree(IR::Function *function)
- : function(function)
- , d(new Data)
- {
- calculateIDoms();
- d.reset();
- }
-
- void computeDF() {
- DF.resize(function->basicBlockCount());
-
- // compute children of each node in the dominator tree
- std::vector<std::vector<BasicBlockIndex> > children; // BasicBlock index -> children
- children.resize(function->basicBlockCount());
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockIndex nodeIndex = n->index();
- Q_ASSERT(function->basicBlock(nodeIndex) == n);
- const BasicBlockIndex nodeDominator = idom[nodeIndex];
- if (nodeDominator == InvalidBasicBlockIndex)
- continue; // there is no dominator to add this node to as a child (e.g. the start node)
- children[nodeDominator].push_back(nodeIndex);
- }
-
- // Fill the worklist and initialize the node status for each basic-block
- std::vector<NodeProgress> nodeStatus;
- nodeStatus.resize(function->basicBlockCount());
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- BasicBlockIndex nodeIndex = bb->index();
- worklist.push_back(nodeIndex);
- NodeProgress &np = nodeStatus[nodeIndex];
- np.children = children[nodeIndex];
- np.todo = children[nodeIndex];
- }
-
- BitVector DF_done(function->basicBlockCount(), false);
-
- while (!worklist.empty()) {
- BasicBlockIndex node = worklist.back();
-
- if (DF_done.at(node)) {
- worklist.pop_back();
- continue;
- }
-
- NodeProgress &np = nodeStatus[node];
- std::vector<BasicBlockIndex>::iterator it = np.todo.begin();
- while (it != np.todo.end()) {
- if (DF_done.at(*it)) {
- it = np.todo.erase(it);
- } else {
- worklist.push_back(*it);
- break;
- }
- }
-
- if (np.todo.empty()) {
- BasicBlockSet &S = DF[node];
- S.init(function);
- for (BasicBlock *y : function->basicBlock(node)->out)
- if (idom[y->index()] != node)
- S.insert(y);
- for (BasicBlockIndex child : np.children) {
- const BasicBlockSet &ws = DF[child];
- for (BasicBlockSet::const_iterator it = ws.begin(), eit = ws.end(); it != eit; ++it) {
- BasicBlock *w = *it;
- const BasicBlockIndex wIndex = w->index();
- if (node == wIndex || !dominates(node, w->index()))
- S.insert(w);
- }
- }
- DF_done.setBit(node);
- worklist.pop_back();
- }
- }
-
- if (DebugDominatorFrontiers) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Dominator Frontiers:" << endl;
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
-
- qout << "\tDF[" << n->index() << "]: {";
- const BasicBlockSet &SList = DF[n->index()];
- for (BasicBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
- if (i != SList.begin())
- qout << ", ";
- qout << (*i)->index();
- }
- qout << "}" << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
- if (DebugDominatorFrontiers && DebugCodeCanUseLotsOfCpu) {
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockSet &fBlocks = DF[n->index()];
- for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) {
- BasicBlock *fBlock = *it;
- Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
- bool hasDominatedSucc = false;
- for (BasicBlock *succ : fBlock->in) {
- if (dominates(n, succ)) {
- hasDominatedSucc = true;
- break;
- }
- }
- if (!hasDominatedSucc) {
- qDebug("%d in DF[%d] has no dominated predecessors", fBlock->index(), n->index());
- }
- Q_ASSERT(hasDominatedSucc);
- }
- }
- }
- }
-
- const BasicBlockSet &dominatorFrontier(BasicBlock *n) const {
- return DF[n->index()];
- }
-
- BasicBlock *immediateDominator(BasicBlock *bb) const {
- const BasicBlockIndex idx = idom[bb->index()];
- if (idx == -1)
- return 0;
- return function->basicBlock(idx);
- }
-
- void dumpImmediateDominators() const
- {
- if (DebugImmediateDominators) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Immediate dominators:" << endl;
- for (BasicBlock *to : function->basicBlocks()) {
- if (to->isRemoved())
- continue;
-
- qout << '\t';
- BasicBlockIndex from = idom.at(to->index());
- if (from != InvalidBasicBlockIndex)
- qout << from;
- else
- qout << "(none)";
- qout << " dominates " << to->index() << endl;
- }
- qDebug("%s", buf.data().constData());
- }
- }
-
- void setImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(!newDominator || newDominator->index() >= 0);
-
- if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index()) >= idom.size()) {
- // This is a new block, probably introduced by edge splitting. So, we'll have to grow
- // the array before inserting the immediate dominator.
- idom.resize(function->basicBlockCount(), InvalidBasicBlockIndex);
- }
-
- const BasicBlockIndex newIdx = newDominator ? newDominator->index() : InvalidBasicBlockIndex;
- if (DebugImmediateDominators)
- qDebug() << "Setting idom of" << bb->index() << "from" << idom[bb->index()] << "to" << newIdx;
- idom[bb->index()] = newIdx;
- }
-
- void collectSiblings(BasicBlock *node, BasicBlockSet &siblings)
- {
- siblings.insert(node);
- const BasicBlockIndex dominator = idom[node->index()];
- if (dominator == InvalidBasicBlockIndex)
- return;
- for (size_t i = 0, ei = idom.size(); i != ei; ++i) {
- if (idom[i] == dominator) {
- BasicBlock *bb = function->basicBlock(int(i));
- if (!bb->isRemoved())
- siblings.insert(bb);
- }
- }
- }
-
- void recalculateIDoms(const BasicBlockSet &nodes, BasicBlock *limit = 0)
- {
- const BasicBlockIndex limitIndex = limit ? limit->index() : InvalidBasicBlockIndex;
- BasicBlockSet todo(nodes), postponed(function);
- while (!todo.empty())
- recalculateIDom(*todo.begin(), todo, postponed, limitIndex);
- }
-
- bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
- return dominates(dominator->index(), dominated->index());
- }
-
- struct Cmp {
- std::vector<int> *nodeDepths;
- Cmp(std::vector<int> *nodeDepths)
- : nodeDepths(nodeDepths)
- { Q_ASSERT(nodeDepths); }
- bool operator()(BasicBlock *one, BasicBlock *two) const
- {
- if (one->isRemoved())
- return false;
- if (two->isRemoved())
- return true;
- return nodeDepths->at(one->index()) > nodeDepths->at(two->index());
- }
- };
-
- // Calculate a depth-first iteration order on the nodes of the dominator tree.
- //
- // The order of the nodes in the vector is not the same as one where a recursive depth-first
- // iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
- // So for the:
- // 1 dominates 2
- // 2 dominates 3
- // 3 dominates 4
- // 2 dominates 5
- // the order will be:
- // 4, 3, 5, 2, 1
- // or:
- // 4, 5, 3, 2, 1
- // So the order of nodes on the same depth is undefined, but it will be after the nodes
- // they dominate, and before the nodes that dominate them.
- //
- // The reason for this order is that a proper DFS pre-/post-order would require inverting
- // the idom vector by either building a real tree datastructure or by searching the idoms
- // for siblings and children. Both have a higher time complexity than sorting by depth.
- QVector<BasicBlock *> calculateDFNodeIterOrder() const
- {
- std::vector<int> depths = calculateNodeDepths();
- QVector<BasicBlock *> order = function->basicBlocks();
- std::sort(order.begin(), order.end(), Cmp(&depths));
- for (int i = 0; i < order.size(); ) {
- if (order[i]->isRemoved())
- order.remove(i);
- else
- ++i;
- }
- return order;
- }
-
- void mergeIntoPredecessor(BasicBlock *successor)
- {
- int succIdx = successor->index();
- if (succIdx == InvalidBasicBlockIndex) {
- return;
- }
-
- int succDom = idom[unsigned(succIdx)];
- for (BasicBlockIndex &idx : idom) {
- if (idx == succIdx) {
- idx = succDom;
- }
- }
- }
-
-private:
- bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const {
- // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
- Q_ASSERT(dominated != InvalidBasicBlockIndex);
-
- if (dominator == dominated)
- return false;
-
- for (BasicBlockIndex it = idom[dominated]; it != InvalidBasicBlockIndex; it = idom[it]) {
- if (it == dominator)
- return true;
- }
-
- return false;
- }
-
- // Algorithm:
- // - for each node:
- // - get the depth of a node. If it's unknown (-1):
- // - get the depth of the immediate dominator.
- // - if that's unknown too, calculate it by calling calculateNodeDepth
- // - set the current node's depth to that of immediate dominator + 1
- std::vector<int> calculateNodeDepths() const
- {
- std::vector<int> nodeDepths(size_t(function->basicBlockCount()), -1);
- nodeDepths[0] = 0;
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int &bbDepth = nodeDepths[bb->index()];
- if (bbDepth == -1) {
- const int immDom = idom[bb->index()];
- int immDomDepth = nodeDepths[immDom];
- if (immDomDepth == -1)
- immDomDepth = calculateNodeDepth(immDom, nodeDepths);
- bbDepth = immDomDepth + 1;
- }
- }
- return nodeDepths;
- }
-
- // Algorithm:
- // - search for the first dominator of a node that has a known depth. As all nodes are
- // reachable from the start node, and that node's depth is 0, this is finite.
- // - while doing that search, put all unknown nodes in the worklist
- // - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
- // node's depth + 1
- // This way every node's depth is calculated once, and the complexity is O(n).
- int calculateNodeDepth(int nodeIdx, std::vector<int> &nodeDepths) const
- {
- std::vector<int> worklist;
- worklist.reserve(8);
- int depth = -1;
-
- do {
- worklist.push_back(nodeIdx);
- nodeIdx = idom[nodeIdx];
- depth = nodeDepths[nodeIdx];
- } while (depth == -1);
-
- for (std::vector<int>::const_reverse_iterator it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
- nodeDepths[*it] = ++depth;
-
- return depth;
- }
-
- // The immediate-dominator recalculation is used when edges are removed from the CFG. See
- // [Ramalingam] for a description. Note that instead of calculating the priority, a recursive
- // algorithm is used: when recalculating the immediate dominator of a node by looking for the
- // least-common ancestor, and a node is hit that also needs recalculation, a recursive call
- // is done to calculate that nodes immediate dominator first.
- //
- // Note that this simplified algorithm cannot cope with back-edges. It only works for
- // non-looping edges (which is our use-case).
- void recalculateIDom(BasicBlock *node, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) {
- Q_ASSERT(!postponed.contains(node));
- Q_ASSERT(todo.contains(node));
- todo.remove(node);
-
- if (node->in.size() == 1) {
- // Special case: if the node has only one incoming edge, then that is the immediate
- // dominator.
- setImmediateDominator(node, node->in.first());
- return;
- }
-
- std::vector<BasicBlockIndex> prefix;
- prefix.reserve(32);
-
- for (BasicBlock *in : node->in) {
- if (node == in) // back-edge to self
- continue;
- if (dominates(node->index(), in->index())) // a known back-edge
- continue;
-
- if (prefix.empty()) {
- calculatePrefix(node, in, prefix, todo, postponed, limit);
-
- if (!prefix.empty()) {
- std::reverse(prefix.begin(), prefix.end());
- Q_ASSERT(!prefix.empty());
- Q_ASSERT(prefix.front() == limit || limit == InvalidBasicBlockIndex);
- }
- } else {
- std::vector<BasicBlockIndex> anotherPrefix;
- anotherPrefix.reserve(prefix.size());
- calculatePrefix(node, in, anotherPrefix, todo, postponed, limit);
-
- if (!anotherPrefix.empty())
- commonPrefix(prefix, anotherPrefix);
- }
- }
-
- Q_ASSERT(!prefix.empty());
- idom[node->index()] = prefix.back();
- }
-
- void calculatePrefix(BasicBlock *node, BasicBlock *in, std::vector<BasicBlockIndex> &prefix, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit)
- {
- for (BasicBlockIndex it = in->index(); it != InvalidBasicBlockIndex; it = idom[it]) {
- prefix.push_back(it);
- if (it == limit)
- return;
- BasicBlock *n = function->basicBlock(it);
- if (postponed.contains(n)) { // possible back-edge, bail out.
- prefix.clear();
- return;
- }
- if (todo.contains(n)) {
- postponed.insert(node);
- recalculateIDom(n, todo, postponed, limit);
- postponed.remove(node);
- }
- }
- }
-
- // Calculate the LCA (Least Common Ancestor) by finding the longest common prefix between two
- // dominator chains. Note that "anotherPrefix" has the node's immediate dominator first, while
- // "bestPrefix" has it last (meaning: is in reverse order). The reason for this is that removing
- // nodes from "bestPrefix" is cheaper because it's done at the end of the vector, while
- // reversing all "anotherPrefix" nodes would take unnecessary time.
- static void commonPrefix(std::vector<BasicBlockIndex> &bestPrefix, const std::vector<BasicBlockIndex> &anotherPrefix)
- {
- const size_t anotherSize = anotherPrefix.size();
- size_t minLen = qMin(bestPrefix.size(), anotherPrefix.size());
- while (minLen != 0) {
- --minLen;
- if (bestPrefix[minLen] == anotherPrefix[anotherSize - minLen - 1]) {
- ++minLen;
- break;
- }
- }
- if (minLen != bestPrefix.size())
- bestPrefix.erase(bestPrefix.begin() + minLen, bestPrefix.end());
- }
-};
-
-class VariableCollector {
- std::vector<Temp> _allTemps;
- std::vector<BasicBlockSet> _defsites;
- std::vector<std::vector<int> > A_orig;
- BitVector nonLocals;
- BitVector killed;
-
- BasicBlock *currentBB;
- bool isCollectable(Temp *t) const
- {
- Q_UNUSED(t);
- Q_ASSERT(t->kind != Temp::PhysicalRegister && t->kind != Temp::StackSlot);
- return true;
- }
-
- void addDefInCurrentBlock(Temp *t)
- {
- std::vector<int> &temps = A_orig[currentBB->index()];
- if (std::find(temps.begin(), temps.end(), t->index) == temps.end())
- temps.push_back(t->index);
- }
-
- void addTemp(Temp *t)
- {
- if (_allTemps[t->index].kind == Temp::Invalid)
- _allTemps[t->index] = *t;
- }
-
-public:
- VariableCollector(IR::Function *function)
- {
- _allTemps.resize(function->tempCount);
- _defsites.resize(function->tempCount);
- for (int i = 0; i < function->tempCount; ++i)
- _defsites[i].init(function);
- nonLocals.resize(function->tempCount);
- const size_t ei = function->basicBlockCount();
- A_orig.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_orig[i].reserve(8);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- currentBB = bb;
- killed.assign(function->tempCount, false);
- for (Stmt *s : bb->statements())
- visit(s);
- }
- }
-
- const std::vector<Temp> &allTemps() const
- { return _allTemps; }
-
- void collectDefSites(const Temp &n, std::vector<BasicBlock *> &bbs) const {
- Q_ASSERT(!n.isInvalid());
- Q_ASSERT(n.index < _defsites.size());
- _defsites[n.index].collectValues(bbs);
- }
-
- const std::vector<int> &inBlock(BasicBlock *n) const
- {
- return A_orig.at(n->index());
- }
-
- bool isNonLocal(const Temp &var) const
- {
- Q_ASSERT(!var.isInvalid());
- Q_ASSERT(static_cast<int>(var.index) < nonLocals.size());
- return nonLocals.at(var.index);
- }
-
-private:
- void visit(Stmt *s)
- {
- if (s->asPhi()) {
- // nothing to do
- } else if (auto move = s->asMove()) {
- visit(move->source);
-
- if (Temp *t = move->target->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- _defsites[t->index].insert(currentBB);
- addDefInCurrentBlock(t);
-
- // For semi-pruned SSA:
- killed.setBit(t->index);
- }
- } else {
- visit(move->target);
- }
- } else {
- STMT_VISIT_ALL_KINDS(s)
- }
- }
-
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- if (!killed.at(t->index)) {
- nonLocals.setBit(t->index);
- }
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-struct UntypedTemp {
- Temp temp;
- UntypedTemp() {}
- UntypedTemp(const Temp &t): temp(t) {}
-};
-inline bool operator==(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return t1.temp.index == t2.temp.index && t1.temp.kind == t2.temp.kind; }
-inline bool operator!=(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-class DefUses
-{
-public:
- struct DefUse {
- DefUse()
- : defStmt(0)
- , blockOfStatement(0)
- { uses.reserve(8); }
- Temp temp;
- Stmt *defStmt;
- BasicBlock *blockOfStatement;
- QVector<Stmt *> uses;
-
- bool isValid() const
- { return temp.kind != Temp::Invalid; }
-
- void clear()
- { defStmt = 0; blockOfStatement = 0; uses.clear(); }
- };
-
-private:
- std::vector<DefUse> _defUses;
- typedef QVarLengthArray<Temp, 4> Temps;
- std::vector<Temps> _usesPerStatement;
-
- void ensure(Temp *newTemp)
- {
- if (_defUses.size() <= newTemp->index) {
- _defUses.resize(newTemp->index + 1);
- }
- }
-
- void ensure(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= _usesPerStatement.size()) {
- _usesPerStatement.resize(s->id() + 1);
- }
- }
-
- void addUseForStatement(Stmt *s, const Temp &var)
- {
- ensure(s);
- _usesPerStatement[s->id()].push_back(var);
- }
-
-public:
- DefUses(IR::Function *function)
- {
- _usesPerStatement.resize(function->statementCount());
- _defUses.resize(function->tempCount);
- }
-
- void cleanup()
- {
- for (size_t i = 0, ei = _defUses.size(); i != ei; ++i) {
- DefUse &defUse = _defUses[i];
- if (defUse.isValid() && !defUse.defStmt)
- defUse.clear();
- }
- }
-
- unsigned statementCount() const
- { return unsigned(_usesPerStatement.size()); }
-
- unsigned tempCount() const
- { return unsigned(_defUses.size()); }
-
- const Temp &temp(int idx) const
- { return _defUses[idx].temp; }
-
- void addDef(Temp *newTemp, Stmt *defStmt, BasicBlock *defBlock)
- {
- ensure(newTemp);
- DefUse &defUse = _defUses[newTemp->index];
- Q_ASSERT(!defUse.isValid());
- defUse.temp = *newTemp;
- defUse.defStmt = defStmt;
- defUse.blockOfStatement = defBlock;
- }
-
- QVector<UntypedTemp> defsUntyped() const
- {
- QVector<UntypedTemp> res;
- res.reserve(tempCount());
- for (const DefUse &du : _defUses)
- if (du.isValid())
- res.append(UntypedTemp(du.temp));
- return res;
- }
-
- std::vector<const Temp *> defs() const {
- std::vector<const Temp *> res;
- const size_t ei = _defUses.size();
- res.reserve(ei);
- for (size_t i = 0; i != ei; ++i) {
- const DefUse &du = _defUses.at(i);
- if (du.isValid())
- res.push_back(&du.temp);
- }
- return res;
- }
-
- void removeDef(const Temp &variable) {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- _defUses[variable.index].clear();
- }
-
- void addUses(const Temp &variable, const QVector<Stmt *> &newUses)
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- for (Stmt *stmt : newUses)
- if (std::find(uses.begin(), uses.end(), stmt) == uses.end())
- uses.push_back(stmt);
- }
-
- void addUse(const Temp &variable, Stmt *newUse)
- {
- if (_defUses.size() <= variable.index) {
- _defUses.resize(variable.index + 1);
- DefUse &du = _defUses[variable.index];
- du.temp = variable;
- du.uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- return;
- }
-
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- if (std::find(uses.begin(), uses.end(), newUse) == uses.end())
- uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- }
-
- int useCount(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].uses.size();
- }
-
- Stmt *defStmt(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].defStmt;
- }
-
- BasicBlock *defStmtBlock(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].blockOfStatement;
- }
-
- void replaceBasicBlock(BasicBlock *from, BasicBlock *to)
- {
- for (auto &du : _defUses) {
- if (du.blockOfStatement == from) {
- du.blockOfStatement = to;
- }
- }
- }
-
- void removeUse(Stmt *usingStmt, const Temp &var)
- {
- Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[var.index].uses;
- uses.erase(std::remove(uses.begin(), uses.end(), usingStmt), uses.end());
- }
-
- void registerNewStatement(Stmt *s)
- {
- ensure(s);
- }
-
- const Temps &usedVars(Stmt *s) const
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(static_cast<unsigned>(s->id()) < _usesPerStatement.size());
- return _usesPerStatement[s->id()];
- }
-
- const QVector<Stmt *> &uses(const Temp &var) const
- {
- return _defUses[var.index].uses;
- }
-
- QVector<Stmt*> removeDefUses(Stmt *s)
- {
- QVector<Stmt*> defStmts;
- for (const Temp &usedVar : usedVars(s)) {
- if (Stmt *ds = defStmt(usedVar))
- defStmts += ds;
- removeUse(s, usedVar);
- }
- if (Move *m = s->asMove()) {
- if (Temp *t = m->target->asTemp())
- removeDef(*t);
- } else if (Phi *p = s->asPhi()) {
- removeDef(*p->targetTemp);
- }
-
- return defStmts;
- }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Defines and uses:" << endl;
- for (const DefUse &du : _defUses) {
- if (!du.isValid())
- continue;
- qout << '%' << du.temp.index;
- qout << " -> defined in block " << du.blockOfStatement->index()
- << ", statement: " << du.defStmt->id()
- << endl;
- qout << " uses:";
- for (Stmt *s : du.uses)
- qout << ' ' << s->id();
- qout << endl;
- }
- qout << "Uses per statement:" << endl;
- for (size_t i = 0, ei = _usesPerStatement.size(); i != ei; ++i) {
- qout << " " << i << ":";
- for (const Temp &t : _usesPerStatement[i])
- qout << ' ' << t.index;
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-};
-
-void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) {
- Phi *phiNode = f->NewStmt<Phi>();
- phiNode->targetTemp = f->New<Temp>();
- phiNode->targetTemp->init(a.kind, a.index);
- y->prependStatement(phiNode);
-
- phiNode->incoming.resize(y->in.size());
- for (int i = 0, ei = y->in.size(); i < ei; ++i) {
- Temp *t = f->New<Temp>();
- t->init(a.kind, a.index);
- phiNode->incoming[i] = t;
- }
-}
-
-// High-level (recursive) algorithm:
-// Mapping: old temp number -> new temp number
-//
-// Start:
-// Rename(start-node)
-//
-// Rename(node, mapping):
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S [1]
-// a_new = generate new/unique temp
-// mapping[a] = a_new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n [2]
-// Rename(X)
-// for each newly generated temp from step [1] restore the old value [3]
-//
-// This algorithm can run out of CPU stack space when there are lots of basic-blocks, like in a
-// switch statement with 8000 cases that all fall-through. The iterativer version below uses a
-// work-item stack, where step [1] from the algorithm above also pushes an "undo mapping change",
-// and step [2] pushes a "rename(X)" action. This eliminates step [3].
-//
-// Iterative version:
-// Mapping: old temp number -> new temp number
-//
-// The stack can hold two kinds of actions:
-// "Rename basic block n"
-// "Restore count for temp"
-//
-// Start:
-// counter = 0
-// push "Rename start node" onto the stack
-// while the stack is not empty:
-// take the last item, and process it
-//
-// Rename(n) =
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S
-// old = mapping[a]
-// push Undo(a, old)
-// counter = counter + 1
-// new = counter;
-// mapping[a] = new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n
-// push Rename(X)
-//
-// Undo(t, c) =
-// mapping[t] = c
-class VariableRenamer
-{
- Q_DISABLE_COPY(VariableRenamer)
-
- IR::Function *function;
- DefUses &defUses;
- unsigned tempCount;
-
- typedef std::vector<int> Mapping; // maps from existing/old temp number to the new and unique temp number.
- enum { Absent = -1 };
- Mapping vregMapping;
- ProcessedBlocks processed;
-
- BasicBlock *currentBB;
- Stmt *currentStmt;
-
- struct TodoAction {
- enum { RestoreVReg, Rename } action;
- union {
- struct {
- unsigned temp;
- int previous;
- } restoreData;
- struct {
- BasicBlock *basicBlock;
- } renameData;
- };
-
- bool isValid() const { return action != Rename || renameData.basicBlock != 0; }
-
- TodoAction()
- {
- action = Rename;
- renameData.basicBlock = 0;
- }
-
- TodoAction(const Temp &t, int prev)
- {
- Q_ASSERT(t.kind == Temp::VirtualRegister);
-
- action = RestoreVReg;
- restoreData.temp = t.index;
- restoreData.previous = prev;
- }
-
- TodoAction(BasicBlock *bb)
- {
- Q_ASSERT(bb);
-
- action = Rename;
- renameData.basicBlock = bb;
- }
- };
-
- QVector<TodoAction> todo;
-
-public:
- VariableRenamer(IR::Function *f, DefUses &defUses)
- : function(f)
- , defUses(defUses)
- , tempCount(0)
- , processed(f)
- {
- vregMapping.assign(f->tempCount, Absent);
- todo.reserve(f->basicBlockCount());
- }
-
- void run() {
- todo.append(TodoAction(function->basicBlock(0)));
-
- while (!todo.isEmpty()) {
- TodoAction todoAction = todo.back();
- Q_ASSERT(todoAction.isValid());
- todo.pop_back();
-
- switch (todoAction.action) {
- case TodoAction::Rename:
- rename(todoAction.renameData.basicBlock);
- break;
- case TodoAction::RestoreVReg:
- restore(vregMapping, todoAction.restoreData.temp, todoAction.restoreData.previous);
- break;
- default:
- Q_UNREACHABLE();
- }
- }
-
- function->tempCount = tempCount;
- }
-
-private:
- static inline void restore(Mapping &mapping, int temp, int previous)
- {
- mapping[temp] = previous;
- }
-
- void rename(BasicBlock *bb)
- {
- while (bb && !processed.alreadyProcessed(bb)) {
- renameStatementsAndPhis(bb);
- processed.markAsProcessed(bb);
-
- BasicBlock *next = 0;
- for (BasicBlock *out : bb->out) {
- if (processed.alreadyProcessed(out))
- continue;
- if (!next)
- next = out;
- else
- todo.append(TodoAction(out));
- }
- bb = next;
- }
- }
-
- void renameStatementsAndPhis(BasicBlock *bb)
- {
- currentBB = bb;
-
- for (Stmt *s : bb->statements()) {
- currentStmt = s;
- visit(s);
- }
-
- for (BasicBlock *Y : bb->out) {
- const int j = Y->in.indexOf(bb);
- Q_ASSERT(j >= 0 && j < Y->in.size());
- for (Stmt *s : Y->statements()) {
- if (Phi *phi = s->asPhi()) {
- Temp *t = phi->incoming[j]->asTemp();
- unsigned newTmp = currentNumber(*t);
-// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index;
- t->index = newTmp;
- t->kind = Temp::VirtualRegister;
- defUses.addUse(*t, phi);
- } else {
- break;
- }
- }
- }
- }
-
- unsigned currentNumber(const Temp &t)
- {
- int nr = Absent;
- switch (t.kind) {
- case Temp::VirtualRegister:
- nr = vregMapping[t.index];
- break;
- default:
- Q_UNREACHABLE();
- nr = Absent;
- break;
- }
- if (nr == Absent) {
- // Special case: we didn't prune the Phi nodes yet, so for proper temps (virtual
- // registers) the SSA algorithm might insert superfluous Phis that have uses without
- // definition. E.g.: if a temporary got introduced in the "then" clause, it "could"
- // reach the "end-if" block, so there will be a phi node for that temp. A later pass
- // will clean this up by looking for uses-without-defines in phi nodes. So, what we do
- // is to generate a new unique number, and leave it dangling.
- nr = nextFreeTemp(t);
- }
-
- return nr;
- }
-
- unsigned nextFreeTemp(const Temp &t)
- {
- unsigned newIndex = tempCount++;
- Q_ASSERT(newIndex <= INT_MAX);
- int oldIndex = Absent;
-
- switch (t.kind) {
- case Temp::VirtualRegister:
- oldIndex = vregMapping[t.index];
- vregMapping[t.index] = newIndex;
- break;
- default:
- Q_UNREACHABLE();
- }
-
- todo.append(TodoAction(t, oldIndex));
-
- return newIndex;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto move = s->asMove()) {
- // uses:
- visit(move->source);
-
- // defs:
- if (Temp *t = move->target->asTemp()) {
- renameTemp(t);
- } else {
- visit(move->target);
- }
- } else if (auto phi = s->asPhi()) {
- renameTemp(phi->targetTemp);
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- temp->index = currentNumber(*temp);
- temp->kind = Temp::VirtualRegister;
- defUses.addUse(*temp, currentStmt);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void renameTemp(Temp *t) { // only called for defs, not uses
- const int newIdx = nextFreeTemp(*t);
-// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx;
- t->kind = Temp::VirtualRegister;
- t->index = newIdx;
- defUses.addDef(t, currentStmt, currentBB);
- }
-};
-
-// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm,
-// see [Appel]. For the changes needed for semi-pruned SSA form, and for its advantages, see [Briggs].
-void convertToSSA(IR::Function *function, const DominatorTree &df, DefUses &defUses)
-{
- // Collect all applicable variables:
- VariableCollector variables(function);
-
- // Prepare for phi node insertion:
- std::vector<BitVector > A_phi;
- const size_t ei = function->basicBlockCount();
- A_phi.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_phi[i].assign(function->tempCount, false);
-
- std::vector<BasicBlock *> W;
- W.reserve(8);
-
- // Place phi functions:
- for (const Temp &a : variables.allTemps()) {
- if (a.isInvalid())
- continue;
- if (!variables.isNonLocal(a))
- continue; // for semi-pruned SSA
-
- W.clear();
- variables.collectDefSites(a, W);
- while (!W.empty()) {
- BasicBlock *n = W.back();
- W.pop_back();
- const BasicBlockSet &dominatorFrontierForN = df.dominatorFrontier(n);
- for (BasicBlockSet::const_iterator it = dominatorFrontierForN.begin(), eit = dominatorFrontierForN.end();
- it != eit; ++it) {
- BasicBlock *y = *it;
- if (!A_phi.at(y->index()).at(a.index)) {
- insertPhiNode(a, y, function);
- A_phi[y->index()].setBit(a.index);
- const std::vector<int> &varsInBlockY = variables.inBlock(y);
- if (std::find(varsInBlockY.begin(), varsInBlockY.end(), a.index) == varsInBlockY.end())
- W.push_back(y);
- }
- }
- }
- }
-
- // Rename variables:
- VariableRenamer(function, defUses).run();
-}
-
-/// Calculate if a phi node result is used only by other phi nodes, and if those uses are
-/// in turn also used by other phi nodes.
-bool hasPhiOnlyUses(Phi *phi, const DefUses &defUses, QBitArray &collectedPhis)
-{
- collectedPhis.setBit(phi->id());
-
- for (Stmt *use : defUses.uses(*phi->targetTemp)) {
- Phi *dependentPhi = use->asPhi();
- if (!dependentPhi)
- return false; // there is a use by a non-phi node
-
- if (collectedPhis.at(dependentPhi->id()))
- continue; // we already found this node
-
- if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis))
- return false;
- }
-
- return true;
-}
-
-void cleanupPhis(DefUses &defUses)
-{
- QBitArray toRemove(defUses.statementCount());
- QBitArray collectedPhis(defUses.statementCount());
- std::vector<Phi *> allPhis;
- allPhis.reserve(32);
-
- for (const Temp *def : defUses.defs()) {
- Stmt *defStmt = defUses.defStmt(*def);
- if (!defStmt)
- continue;
-
- Phi *phi = defStmt->asPhi();
- if (!phi)
- continue;
- allPhis.push_back(phi);
- if (toRemove.at(phi->id()))
- continue;
-
- collectedPhis.fill(false);
- if (hasPhiOnlyUses(phi, defUses, collectedPhis))
- toRemove |= collectedPhis;
- }
-
- for (Phi *phi : allPhis) {
- if (!toRemove.at(phi->id()))
- continue;
-
- const Temp &targetVar = *phi->targetTemp;
- defUses.defStmtBlock(targetVar)->removeStatement(phi);
-
- for (const Temp &usedVar : defUses.usedVars(phi))
- defUses.removeUse(phi, usedVar);
- defUses.removeDef(targetVar);
- }
-
- defUses.cleanup();
-}
-
-class StatementWorklist
-{
- IR::Function *theFunction;
- std::vector<Stmt *> stmts;
- BitVector worklist;
- unsigned worklistSize;
- std::vector<int> replaced;
- BitVector removed;
-
- Q_DISABLE_COPY(StatementWorklist)
-
-public:
- StatementWorklist(IR::Function *function)
- : theFunction(function)
- , stmts(function->statementCount(), static_cast<Stmt *>(0))
- , worklist(function->statementCount(), false)
- , worklistSize(0)
- , replaced(function->statementCount(), Stmt::InvalidId)
- , removed(function->statementCount())
- {
- grow();
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- stmts[s->id()] = s;
- worklist.setBit(s->id());
- ++worklistSize;
- }
- }
- }
-
- void reset()
- {
- worklist.assign(worklist.size(), false);
- worklistSize = 0;
-
- for (Stmt *s : stmts) {
- if (!s)
- continue;
-
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- void remove(Stmt *stmt)
- {
- replaced[stmt->id()] = Stmt::InvalidId;
- removed.setBit(stmt->id());
- if (worklist.at(stmt->id())) {
- worklist.clearBit(stmt->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
- }
-
- void replace(Stmt *oldStmt, Stmt *newStmt)
- {
- Q_ASSERT(oldStmt);
- Q_ASSERT(replaced[oldStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(oldStmt->id()) == false);
-
- Q_ASSERT(newStmt);
- registerNewStatement(newStmt);
- Q_ASSERT(replaced[newStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(newStmt->id()) == false);
-
- replaced[oldStmt->id()] = newStmt->id();
- worklist.clearBit(oldStmt->id());
- }
-
- void applyToFunction()
- {
- for (BasicBlock *bb : theFunction->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (int i = 0; i < bb->statementCount();) {
- Stmt *stmt = bb->statements().at(i);
-
- int id = stmt->id();
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- for (int replacementId = replaced[id]; replacementId != Stmt::InvalidId; replacementId = replaced[replacementId])
- id = replacementId;
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- if (removed.at(id)) {
- bb->removeStatement(i);
- } else {
- if (id != stmt->id())
- bb->replaceStatement(i, stmts[id]);
-
- ++i;
- }
- }
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- StatementWorklist &operator+=(const QVector<Stmt *> &stmts)
- {
- for (Stmt *s : stmts)
- this->operator+=(s);
-
- return *this;
- }
-
- StatementWorklist &operator+=(Stmt *s)
- {
- if (!s)
- return *this;
-
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (!worklist.at(s->id())) {
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- return *this;
- }
-
- StatementWorklist &operator-=(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (worklist.at(s->id())) {
- worklist.clearBit(s->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
-
- return *this;
- }
-
- unsigned size() const
- {
- return worklistSize;
- }
-
- Stmt *takeNext(Stmt *last)
- {
- if (worklistSize == 0)
- return 0;
-
- const int startAt = last ? last->id() + 1 : 0;
- Q_ASSERT(startAt >= 0);
- Q_ASSERT(startAt <= worklist.size());
-
- Q_ASSERT(static_cast<size_t>(worklist.size()) == stmts.size());
-
- int pos = worklist.findNext(startAt, true, /*wrapAround = */true);
-
- worklist.clearBit(pos);
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- Stmt *s = stmts.at(pos);
- Q_ASSERT(s);
-
- if (removed.at(s->id()))
- return takeNext(s);
-
- return s;
- }
-
- IR::Function *function() const
- {
- return theFunction;
- }
-
- void registerNewStatement(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= stmts.size()) {
- if (static_cast<unsigned>(s->id()) >= stmts.capacity())
- grow();
-
- int newSize = s->id() + 1;
- stmts.resize(newSize, 0);
- worklist.resize(newSize);
- replaced.resize(newSize, Stmt::InvalidId);
- removed.resize(newSize);
- }
-
- stmts[s->id()] = s;
- }
-
-private:
- void grow()
- {
- Q_ASSERT(stmts.capacity() < INT_MAX / 2);
- int newCapacity = ((static_cast<int>(stmts.capacity()) + 1) * 3) / 2;
- stmts.reserve(newCapacity);
- worklist.reserve(newCapacity);
- replaced.reserve(newCapacity);
- removed.reserve(newCapacity);
- }
-};
-
-class SideEffectsChecker
-{
- bool _sideEffect;
-
-public:
- SideEffectsChecker()
- : _sideEffect(false)
- {}
-
- ~SideEffectsChecker()
- {}
-
- bool hasSideEffects(Expr *expr)
- {
- bool sideEffect = false;
- qSwap(_sideEffect, sideEffect);
- visit(expr);
- qSwap(_sideEffect, sideEffect);
- return sideEffect;
- }
-
-protected:
- void markAsSideEffect()
- {
- _sideEffect = true;
- }
-
- bool seenSideEffects() const { return _sideEffect; }
-
- void visit(Expr *e)
- {
- if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- virtual void visitTemp(Temp *) {}
-
-private:
- void visitName(Name *e) {
- if (e->freeOfSideEffects)
- return;
- // TODO: maybe we can distinguish between built-ins of which we know that they do not have
- // a side-effect.
- if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QLatin1String("this")))
- markAsSideEffect();
- }
-
- void visitClosure(Closure *) {
- markAsSideEffect();
- }
-
- void visitConvert(Convert *e) {
- visit(e->expr);
-
- switch (e->expr->type) {
- case QObjectType:
- case StringType:
- case VarType:
- markAsSideEffect();
- break;
- default:
- break;
- }
- }
-
- void visitUnop(Unop *e) {
- visit(e->expr);
-
- switch (e->op) {
- case OpUPlus:
- case OpUMinus:
- case OpNot:
- case OpIncrement:
- case OpDecrement:
- if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType)
- markAsSideEffect();
- break;
-
- default:
- break;
- }
- }
-
- void visitBinop(Binop *e) {
- // TODO: prune parts that don't have a side-effect. For example, in:
- // function f(x) { +x+1; return 0; }
- // we can prune the binop and leave the unop/conversion.
- _sideEffect = hasSideEffects(e->left);
- _sideEffect |= hasSideEffects(e->right);
-
- if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType
- || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType)
- markAsSideEffect();
- }
-
- void visitSubscript(Subscript *e) {
- visit(e->base);
- visit(e->index);
- markAsSideEffect();
- }
-
- void visitMember(Member *e) {
- visit(e->base);
- if (e->freeOfSideEffects)
- return;
- markAsSideEffect();
- }
-
- void visitCall(Call *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in functions that have no side effect.
- }
-
- void visitNew(New *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in types that have no side effect.
- }
-};
-
-class EliminateDeadCode: public SideEffectsChecker
-{
- DefUses &_defUses;
- StatementWorklist &_worklist;
- QVarLengthArray<Temp *, 8> _collectedTemps;
-
-public:
- EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist)
- : _defUses(defUses)
- , _worklist(worklist)
- {}
-
- void run(Expr *&expr, Stmt *stmt) {
- _collectedTemps.clear();
- if (!hasSideEffects(expr)) {
- expr = 0;
- for (Temp *t : _collectedTemps) {
- _defUses.removeUse(stmt, *t);
- _worklist += _defUses.defStmt(*t);
- }
- }
- }
-
-protected:
- void visitTemp(Temp *e) override final
- {
- _collectedTemps.append(e);
- }
-};
-
-class PropagateTempTypes
-{
- const DefUses &defUses;
- UntypedTemp theTemp;
- DiscoveredType newType;
-
-public:
- PropagateTempTypes(const DefUses &defUses)
- : defUses(defUses)
- {}
-
- void run(const UntypedTemp &temp, const DiscoveredType &type)
- {
- newType = type;
- theTemp = temp;
- if (Stmt *defStmt = defUses.defStmt(temp.temp))
- visit(defStmt);
- for (Stmt *use : defUses.uses(temp.temp))
- visit(use);
- }
-
-private:
- void visit(Stmt *s)
- {
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- if (theTemp == UntypedTemp(*temp)) {
- temp->type = static_cast<Type>(newType.type);
- temp->memberResolver = newType.memberResolver;
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-class TypeInference
-{
- enum { DebugTypeInference = 0 };
-
- QQmlEnginePrivate *qmlEngine;
- const DefUses &_defUses;
- typedef std::vector<DiscoveredType> TempTypes;
- TempTypes _tempTypes;
- StatementWorklist *_worklist;
- struct TypingResult {
- DiscoveredType type;
- bool fullyTyped;
-
- TypingResult(const DiscoveredType &type = DiscoveredType()) {
-#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6
- // avoid optimization bug in gcc 4.6.3 armhf
- ((int volatile &) this->type.type) = type.type;
-#endif
- this->type = type;
- fullyTyped = type.type != UnknownType;
- }
- explicit TypingResult(MemberExpressionResolver *memberResolver)
- : type(memberResolver)
- , fullyTyped(true)
- {}
- };
- TypingResult _ty;
- Stmt *_currentStmt;
-
-public:
- TypeInference(QQmlEnginePrivate *qmlEngine, const DefUses &defUses)
- : qmlEngine(qmlEngine)
- , _defUses(defUses)
- , _tempTypes(_defUses.tempCount())
- , _worklist(0)
- , _ty(UnknownType)
- , _currentStmt(nullptr)
- {}
-
- void run(StatementWorklist &w) {
- _worklist = &w;
-
- Stmt *s = 0;
- while ((s = _worklist->takeNext(s))) {
- if (s->asJump())
- continue;
-
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Typing stmt ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
-
- qDebug("%u left in the worklist", _worklist->size());
- }
-
- if (!run(s)) {
- *_worklist += s;
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Pushing back stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- } else {
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Finished: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
- }
-
- PropagateTempTypes propagator(_defUses);
- for (size_t i = 0, ei = _tempTypes.size(); i != ei; ++i) {
- const Temp &temp = _defUses.temp(int(i));
- if (temp.kind == Temp::Invalid)
- continue;
- const DiscoveredType &tempType = _tempTypes[i];
- if (tempType.type == UnknownType)
- continue;
- propagator.run(temp, tempType);
- }
-
- _worklist = 0;
- }
-
-private:
- bool run(Stmt *s) {
- TypingResult ty;
- std::swap(_ty, ty);
- std::swap(_currentStmt, s);
- visit(_currentStmt);
- std::swap(_currentStmt, s);
- std::swap(_ty, ty);
- return ty.fullyTyped;
- }
-
- TypingResult run(Expr *e) {
- TypingResult ty;
- std::swap(_ty, ty);
- visit(e);
- std::swap(_ty, ty);
-
- if (ty.type != UnknownType)
- setType(e, ty.type);
- return ty;
- }
-
- void setType(Expr *e, DiscoveredType ty) {
- if (Temp *t = e->asTemp()) {
- if (DebugTypeInference)
- qDebug() << "Setting type for temp" << t->index
- << " to " << typeName(Type(ty.type)) << "(" << ty.type << ")"
- << endl;
-
- DiscoveredType &it = _tempTypes[t->index];
- if (it != ty) {
- it = ty;
-
- if (DebugTypeInference) {
- for (Stmt *s : _defUses.uses(*t)) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Pushing back dependent stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
-
- for (Stmt *s : qAsConst(_defUses.uses(*t))) {
- if (s != _currentStmt) {
- *_worklist += s;
- }
- }
- }
- } else {
- e->type = (Type) ty.type;
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *c) {
- if (c->type & NumberType) {
- if (canConvertToSignedInteger(c->value))
- _ty = TypingResult(SInt32Type);
- else if (canConvertToUnsignedInteger(c->value))
- _ty = TypingResult(UInt32Type);
- else
- _ty = TypingResult(c->type);
- } else
- _ty = TypingResult(c->type);
- }
- void visitString(IR::String *) { _ty = TypingResult(StringType); }
- void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
- void visitName(Name *) { _ty = TypingResult(VarType); }
- void visitTemp(Temp *e) {
- if (e->memberResolver && e->memberResolver->isValid())
- _ty = TypingResult(e->memberResolver);
- else
- _ty = TypingResult(_tempTypes[e->index]);
- setType(e, _ty.type);
- }
- void visitArgLocal(ArgLocal *e) {
- _ty = TypingResult(VarType);
- setType(e, _ty.type);
- }
-
- void visitClosure(Closure *) { _ty = TypingResult(VarType); }
- void visitConvert(Convert *e) {
- _ty = TypingResult(e->type);
- }
-
- void visitUnop(Unop *e) {
- _ty = run(e->expr);
- switch (e->op) {
- case OpUPlus: _ty.type = DoubleType; return;
- case OpUMinus: _ty.type = DoubleType; return;
- case OpCompl: _ty.type = SInt32Type; return;
- case OpNot: _ty.type = BoolType; return;
-
- case OpIncrement:
- case OpDecrement:
- Q_ASSERT(!"Inplace operators should have been removed!");
- Q_UNREACHABLE();
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitBinop(Binop *e) {
- TypingResult leftTy = run(e->left);
- TypingResult rightTy = run(e->right);
- _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped;
-
- switch (e->op) {
- case OpAdd:
- if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType))
- _ty.type = VarType;
- else if (leftTy.type.test(StringType) || rightTy.type.test(StringType))
- _ty.type = StringType;
- else if (leftTy.type != UnknownType && rightTy.type != UnknownType)
- _ty.type = DoubleType;
- else
- _ty.type = UnknownType;
- break;
- case OpSub:
- _ty.type = DoubleType;
- break;
-
- case OpMul:
- case OpDiv:
- case OpMod:
- _ty.type = DoubleType;
- break;
-
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- _ty.type = SInt32Type;
- break;
- case OpURShift:
- _ty.type = UInt32Type;
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpAnd:
- case OpOr:
- case OpInstanceof:
- case OpIn:
- _ty.type = BoolType;
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitCall(Call *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitNew(New *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitSubscript(Subscript *e) {
- _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
- _ty.type = VarType;
- }
-
- void visitMember(Member *e) {
- _ty = run(e->base);
-
- if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) {
- MemberExpressionResolver *resolver = _ty.type.memberResolver;
- _ty.type = resolver->resolveMember(qmlEngine, resolver, e);
- } else
- _ty.type = VarType;
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { _ty = run(s->expr); }
- void visitMove(Move *s) {
- if (Temp *t = s->target->asTemp()) {
- if (Name *n = s->source->asName()) {
- if (n->builtin == Name::builtin_qml_context) {
- _ty = TypingResult(t->memberResolver);
- setType(n, _ty.type);
- setType(t, _ty.type);
- return;
- }
- }
- TypingResult sourceTy = run(s->source);
- setType(t, sourceTy.type);
- _ty = sourceTy;
- return;
- }
-
- TypingResult sourceTy = run(s->source);
- _ty = run(s->target);
- _ty.fullyTyped &= sourceTy.fullyTyped;
- }
-
- void visitJump(Jump *) { _ty = TypingResult(MissingType); }
- void visitCJump(CJump *s) { _ty = run(s->cond); }
- void visitRet(Ret *s) { _ty = run(s->expr); }
- void visitPhi(Phi *s) {
- _ty = run(s->incoming[0]);
- for (int i = 1, ei = s->incoming.size(); i != ei; ++i) {
- TypingResult ty = run(s->incoming[i]);
- if (!ty.fullyTyped && _ty.fullyTyped) {
- // When one of the temps not fully typed, we already know that we cannot completely type this node.
- // So, pick the type we calculated upto this point, and wait until the unknown one will be typed.
- // At that point, this statement will be re-scheduled, and then we can fully type this node.
- _ty.fullyTyped = false;
- break;
- }
- _ty.type.type |= ty.type.type;
- _ty.fullyTyped &= ty.fullyTyped;
- if (_ty.type.test(QObjectType) && _ty.type.memberResolver)
- _ty.type.memberResolver->clear(); // ### TODO: find common ancestor meta-object
- }
-
- switch (_ty.type.type) {
- case UnknownType:
- case UndefinedType:
- case NullType:
- case BoolType:
- case SInt32Type:
- case UInt32Type:
- case DoubleType:
- case StringType:
- case QObjectType:
- case VarType:
- // The type is not a combination of two or more types, so we're done.
- break;
-
- default:
- // There are multiple types involved, so:
- if (_ty.type.isNumber())
- // The type is any combination of double/int32/uint32, but nothing else. So we can
- // type it as double.
- _ty.type = DoubleType;
- else
- // There just is no single type that can hold this combination, so:
- _ty.type = VarType;
- }
-
- setType(s->targetTemp, _ty.type);
- }
-};
-
-class ReverseInference
-{
- const DefUses &_defUses;
-
-public:
- ReverseInference(const DefUses &defUses)
- : _defUses(defUses)
- {}
-
- void run(IR::Function *f)
- {
- Q_UNUSED(f);
-
- QVector<UntypedTemp> knownOk;
- QVector<UntypedTemp> candidates = _defUses.defsUntyped();
- while (!candidates.isEmpty()) {
- UntypedTemp temp = candidates.last();
- candidates.removeLast();
-
- if (knownOk.contains(temp))
- continue;
-
- if (!isUsedAsInt32(temp, knownOk))
- continue;
-
- Stmt *s = _defUses.defStmt(temp.temp);
- Move *m = s->asMove();
- if (!m)
- continue;
- Temp *target = m->target->asTemp();
- if (!target || temp != UntypedTemp(*target) || target->type == SInt32Type)
- continue;
- if (Temp *t = m->source->asTemp()) {
- candidates.append(*t);
- } else if (m->source->asConvert()) {
- break;
- } else if (Binop *b = m->source->asBinop()) {
- bool iterateOnOperands = true;
-
- switch (b->op) {
- case OpSub:
- case OpMul:
- case OpAdd:
- if (b->left->type == SInt32Type && b->right->type == SInt32Type) {
- iterateOnOperands = false;
- break;
- } else {
- continue;
- }
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- case OpURShift:
- break;
- default:
- continue;
- }
-
- if (iterateOnOperands) {
- if (Temp *lt = b->left->asTemp())
- candidates.append(*lt);
- if (Temp *rt = b->right->asTemp())
- candidates.append(*rt);
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpCompl || u->op == OpUPlus) {
- if (Temp *t = u->expr->asTemp())
- candidates.append(*t);
- }
- } else {
- continue;
- }
-
- knownOk.append(temp);
- }
-
- PropagateTempTypes propagator(_defUses);
- for (const UntypedTemp &t : qAsConst(knownOk)) {
- propagator.run(t, SInt32Type);
- if (Stmt *defStmt = _defUses.defStmt(t.temp)) {
- if (Move *m = defStmt->asMove()) {
- if (Convert *c = m->source->asConvert()) {
- c->type = SInt32Type;
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op != OpUMinus)
- u->type = SInt32Type;
- } else if (Binop *b = m->source->asBinop()) {
- b->type = SInt32Type;
- }
- }
- }
- }
- }
-
-private:
- bool isUsedAsInt32(const UntypedTemp &t, const QVector<UntypedTemp> &knownOk) const
- {
- const QVector<Stmt *> &uses = _defUses.uses(t.temp);
- if (uses.isEmpty())
- return false;
-
- for (Stmt *use : uses) {
- if (Move *m = use->asMove()) {
- Temp *targetTemp = m->target->asTemp();
-
- if (m->source->asTemp()) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (m->source->asConvert()) {
- continue;
- } else if (Binop *b = m->source->asBinop()) {
- switch (b->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- Q_FALLTHROUGH();
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpRShift:
- case OpLShift:
- case OpURShift:
- continue;
- default:
- return false;
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (u->op != OpCompl) {
- return false;
- }
- } else {
- return false;
- }
- } else
- return false;
- }
-
- return true;
- }
-};
-
-void convertConst(Const *c, Type targetType)
-{
- switch (targetType) {
- case DoubleType:
- break;
- case SInt32Type:
- c->value = QV4::Primitive::toInt32(c->value);
- break;
- case UInt32Type:
- c->value = QV4::Primitive::toUInt32(c->value);
- break;
- case BoolType:
- c->value = !(c->value == 0 || std::isnan(c->value));
- break;
- case NullType:
- case UndefinedType:
- c->value = qt_qnan();
- c->type = targetType;
- break;
- default:
- Q_UNIMPLEMENTED();
- Q_ASSERT(!"Unimplemented!");
- break;
- }
- c->type = targetType;
-}
-
-class TypePropagation
-{
- DefUses &_defUses;
- Type _ty;
- IR::Function *_f;
-
- bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) {
- qSwap(_ty, requestedType);
- visit(e);
- qSwap(_ty, requestedType);
-
- if (requestedType != UnknownType) {
- if (e->type != requestedType) {
- if (requestedType & NumberType || requestedType == BoolType) {
- if (insertConversion)
- addConversion(e, requestedType);
- return true;
- }
- }
- }
-
- return false;
- }
-
- struct Conversion {
- Expr **expr;
- Type targetType;
- Stmt *stmt;
-
- Conversion(Expr **expr = 0, Type targetType = UnknownType, Stmt *stmt = 0)
- : expr(expr)
- , targetType(targetType)
- , stmt(stmt)
- {}
- };
-
- Stmt *_currStmt;
- QVector<Conversion> _conversions;
-
- void addConversion(Expr *&expr, Type targetType) {
- _conversions.append(Conversion(&expr, targetType, _currStmt));
- }
-
-public:
- TypePropagation(DefUses &defUses) : _defUses(defUses), _ty(UnknownType) {}
-
- void run(IR::Function *f, StatementWorklist &worklist) {
- _f = f;
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _conversions.clear();
-
- for (Stmt *s : bb->statements()) {
- _currStmt = s;
- visit(s);
- }
-
- for (const Conversion &conversion : qAsConst(_conversions)) {
- IR::Move *move = conversion.stmt->asMove();
-
- // Note: isel only supports move into member when source is a temp, so convert
- // is not a supported source.
- if (move && move->source->asTemp() && !move->target->asMember()) {
- *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType);
- } else if (Const *c = (*conversion.expr)->asConst()) {
- convertConst(c, conversion.targetType);
- } else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) {
- Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(al, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.addUse(*source, conversion.stmt);
-
- if (conversion.stmt->asPhi()) {
- // Only temps can be used as arguments to phi nodes, so this is a sanity check...:
- Q_UNREACHABLE();
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Temp *t = (*conversion.expr)->asTemp()) {
- Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(t, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
- _defUses.addUse(*t, convCall);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.removeUse(conversion.stmt, *t);
- _defUses.addUse(*source, conversion.stmt);
-
- if (Phi *phi = conversion.stmt->asPhi()) {
- int idx = phi->incoming.indexOf(t);
- Q_ASSERT(idx != -1);
- bb->in[idx]->insertStatementBeforeTerminator(convCall);
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Unop *u = (*conversion.expr)->asUnop()) {
- // convert:
- // int32{%2} = double{-double{%1}};
- // to:
- // double{%3} = double{-double{%1}};
- // int32{%2} = int32{convert(double{%3})};
- Temp *tmp = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
- tmp->type = u->type;
- Move *extraMove = f->NewStmt<Move>();
- worklist.registerNewStatement(extraMove);
- extraMove->init(tmp, u);
- _defUses.addDef(tmp, extraMove, bb);
-
- if (Temp *unopOperand = u->expr->asTemp()) {
- _defUses.addUse(*unopOperand, extraMove);
- _defUses.removeUse(move, *unopOperand);
- }
-
- bb->insertStatementBefore(conversion.stmt, extraMove);
-
- *conversion.expr = bb->CONVERT(CloneExpr::cloneTemp(tmp, f), conversion.targetType);
- _defUses.addUse(*tmp, move);
- } else {
- Q_UNREACHABLE();
- }
- }
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto c = e->asConvert()) {
- run(c->expr, c->type);
- } else if (auto u = e->asUnop()) {
- run(u->expr, u->type);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- void visitConst(Const *c) {
- if (_ty & NumberType && c->type & NumberType) {
- if (_ty == SInt32Type)
- c->value = QV4::Primitive::toInt32(c->value);
- else if (_ty == UInt32Type)
- c->value = QV4::Primitive::toUInt32(c->value);
- c->type = _ty;
- }
- }
-
- void visitBinop(Binop *e) {
- // FIXME: This routine needs more tuning!
- switch (e->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- case OpDiv:
- case OpMod:
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- run(e->left, e->type);
- run(e->right, e->type);
- break;
-
- case OpLShift:
- case OpRShift:
- case OpURShift:
- run(e->left, SInt32Type);
- run(e->right, SInt32Type);
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- if (e->left->type == DoubleType) {
- run(e->right, DoubleType);
- } else if (e->right->type == DoubleType) {
- run(e->left, DoubleType);
- } else {
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- }
- break;
-
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpInstanceof:
- case OpIn:
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
- void visitCall(Call *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitNew(New *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
- void visitMember(Member *e) { run(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- }
- }
-
- void visitExp(Exp *s) { run(s->expr); }
- void visitMove(Move *s) {
- if (s->source->asConvert())
- return; // this statement got inserted for a phi-node type conversion
-
- run(s->target);
-
- if (Unop *u = s->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (run(u->expr, s->target->type, false)) {
- Convert *convert = _f->New<Convert>();
- convert->init(u->expr, s->target->type);
- s->source = convert;
- } else {
- s->source = u->expr;
- }
-
- return;
- }
- }
-
- const Member *targetMember = s->target->asMember();
- const bool inhibitConversion = targetMember && targetMember->inhibitTypeConversionOnWrite;
-
- run(s->source, s->target->type, !inhibitConversion);
- }
- void visitCJump(CJump *s) {
- run(s->cond, BoolType);
- }
- void visitRet(Ret *s) { run(s->expr); }
- void visitPhi(Phi *s) {
- Type ty = s->targetTemp->type;
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- run(s->incoming[i], ty);
- }
-};
-
-void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &worklist, DefUses &defUses)
-{
- const QVector<BasicBlock *> copy = f->basicBlocks();
- for (BasicBlock *toBB : copy) {
- if (toBB->isRemoved())
- continue;
- if (toBB->in.size() < 2)
- continue;
-
- for (int inIdx = 0, eInIdx = toBB->in.size(); inIdx != eInIdx; ++inIdx) {
- BasicBlock *fromBB = toBB->in[inIdx];
- if (fromBB->out.size() < 2)
- continue;
-
- // We found a critical edge.
- // create the basic block:
- BasicBlock *newBB = f->newBasicBlock(toBB->catchBlock);
- Jump *s = f->NewStmt<Jump>();
- worklist.registerNewStatement(s);
- defUses.registerNewStatement(s);
- s->init(toBB);
- newBB->appendStatement(s);
-
- // rewire the old outgoing edge
- int outIdx = fromBB->out.indexOf(toBB);
- fromBB->out[outIdx] = newBB;
- newBB->in.append(fromBB);
-
- // rewire the old incoming edge
- toBB->in[inIdx] = newBB;
- newBB->out.append(toBB);
-
- // add newBB to the correct loop group
- if (toBB->isGroupStart()) {
- if (fromBB == toBB) {
- // special case: the loop header points back to itself (so it's a small loop).
- newBB->setContainingGroup(toBB);
- } else {
- BasicBlock *container;
- for (container = fromBB->containingGroup(); container; container = container->containingGroup())
- if (container == toBB)
- break;
- if (container == toBB) // if we were already inside the toBB loop
- newBB->setContainingGroup(toBB);
- else
- newBB->setContainingGroup(toBB->containingGroup());
- }
- } else {
- newBB->setContainingGroup(toBB->containingGroup());
- }
-
- // patch the terminator
- Stmt *terminator = fromBB->terminator();
- if (Jump *j = terminator->asJump()) {
- Q_ASSERT(outIdx == 0);
- j->target = newBB;
- } else if (CJump *j = terminator->asCJump()) {
- if (outIdx == 0)
- j->iftrue = newBB;
- else if (outIdx == 1)
- j->iffalse = newBB;
- else
- Q_ASSERT(!"Invalid out edge index for CJUMP!");
- } else if (terminator->asRet()) {
- Q_ASSERT(!"A block with a RET at the end cannot have outgoing edges.");
- } else {
- Q_ASSERT(!"Unknown terminator!");
- }
-
-// qDebug() << "splitting edge" << fromBB->index() << "->" << toBB->index()
-// << "by inserting block" << newBB->index();
-
- // Set the immediate dominator of the new block to inBB
- df.setImmediateDominator(newBB, fromBB);
-
- bool toNeedsNewIdom = true;
- for (BasicBlock *bb : toBB->in) {
- if (bb != newBB && bb != toBB && !df.dominates(toBB, bb)) {
- toNeedsNewIdom = false;
- break;
- }
- }
- if (toNeedsNewIdom)
- df.setImmediateDominator(toBB, newBB);
- }
- }
-}
-
-// Detect all (sub-)loops in a function.
-//
-// Doing loop detection on the CFG is better than relying on the statement information in
-// order to mark loops. Although JavaScript only has natural loops, it can still be the case
-// that something is not a loop even though a loop-like-statement is in the source. For
-// example:
-// while (true) {
-// if (i > 0)
-// break;
-// else
-// break;
-// }
-//
-// Algorithm:
-// - do a DFS on the dominator tree, where for each node:
-// - collect all back-edges
-// - if there are back-edges, the node is a loop-header for a new loop, so:
-// - walk the CFG is reverse-direction, and for every node:
-// - if the node already belongs to a loop, we've found a nested loop:
-// - get the loop-header for the (outermost) nested loop
-// - add that loop-header to the current loop
-// - continue by walking all incoming edges that do not yet belong to the current loop
-// - if the node does not belong to a loop yet, add it to the current loop, and
-// go on with all incoming edges
-//
-// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
-// an incoming edge where the other node is dominated by the current node. Meaning: all
-// execution paths that reach that other node have to go through the current node, that other
-// node ends with a (conditional) jump back to the loop header.
-//
-// The exact order of the DFS on the dominator tree is not important. The only property has to
-// be that a node is only visited when all the nodes it dominates have been visited before.
-// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
-// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
-// their containing loops, which makes nested-loop identification free. An added benefit is
-// that the nodes for those sub-loops are only processed once.
-//
-// Note: independent loops that share the same header are merged together. For example, in
-// the code snippet below, there are 2 back-edges into the loop-header, but only one single
-// loop will be detected.
-// while (a) {
-// if (b)
-// continue;
-// else
-// continue;
-// }
-class LoopDetection
-{
- enum { DebugLoopDetection = 0 };
-
- Q_DISABLE_COPY(LoopDetection)
-
-public:
- struct LoopInfo
- {
- BasicBlock *loopHeader;
- QVector<BasicBlock *> loopBody;
- QVector<LoopInfo *> nestedLoops;
- LoopInfo *parentLoop;
-
- LoopInfo(BasicBlock *loopHeader = 0)
- : loopHeader(loopHeader)
- , parentLoop(0)
- {}
-
- bool isValid() const
- { return loopHeader != 0; }
-
- void addNestedLoop(LoopInfo *nested)
- {
- Q_ASSERT(nested);
- Q_ASSERT(!nestedLoops.contains(nested));
- Q_ASSERT(nested->parentLoop == 0);
- nested->parentLoop = this;
- nestedLoops.append(nested);
- }
- };
-
-public:
- LoopDetection(const DominatorTree &dt)
- : dt(dt)
- {}
-
- ~LoopDetection()
- {
- qDeleteAll(loopInfos);
- }
-
- void run(IR::Function *function)
- {
- std::vector<BasicBlock *> backedges;
- backedges.reserve(4);
-
- const auto order = dt.calculateDFNodeIterOrder();
- for (BasicBlock *bb : order) {
- Q_ASSERT(!bb->isRemoved());
-
- backedges.clear();
-
- for (BasicBlock *in : bb->in)
- if (bb == in || dt.dominates(bb, in))
- backedges.push_back(in);
-
- if (!backedges.empty()) {
- subLoop(bb, backedges);
- }
- }
-
- createLoopInfos(function);
- dumpLoopInfo();
- }
-
- void dumpLoopInfo() const
- {
- if (!DebugLoopDetection)
- return;
-
- qDebug() << "Found" << loopInfos.size() << "loops";
- for (const LoopInfo *info : loopInfos) {
- qDebug() << "Loop header:" << info->loopHeader->index()
- << "for loop" << quint64(info);
- for (BasicBlock *bb : info->loopBody)
- qDebug() << " " << bb->index();
- for (LoopInfo *nested : info->nestedLoops)
- qDebug() << " sub loop:" << quint64(nested);
- qDebug() << " parent loop:" << quint64(info->parentLoop);
- }
- }
-
- QVector<LoopInfo *> allLoops() const
- { return loopInfos; }
-
- // returns all loop headers for loops that have no nested loops.
- QVector<LoopInfo *> innermostLoops() const
- {
- QVector<LoopInfo *> inner(loopInfos);
-
- for (int i = 0; i < inner.size(); ) {
- if (inner.at(i)->nestedLoops.isEmpty())
- ++i;
- else
- inner.remove(i);
- }
-
- return inner;
- }
-
-private:
- void subLoop(BasicBlock *loopHead, const std::vector<BasicBlock *> &backedges)
- {
- loopHead->markAsGroupStart();
- LoopInfo *info = new LoopInfo;
- info->loopHeader = loopHead;
- loopInfos.append(info);
-
- std::vector<BasicBlock *> worklist;
- worklist.reserve(backedges.size() + 8);
- worklist.insert(worklist.end(), backedges.begin(), backedges.end());
- while (!worklist.empty()) {
- BasicBlock *predIt = worklist.back();
- worklist.pop_back();
-
- BasicBlock *subloop = predIt->containingGroup();
- if (subloop) {
- // This is a discovered block. Find its outermost discovered loop.
- while (BasicBlock *parentLoop = subloop->containingGroup())
- subloop = parentLoop;
-
- // If it is already discovered to be a subloop of this loop, continue.
- if (subloop == loopHead)
- continue;
-
- // Yay, it's a subloop of this loop.
- subloop->setContainingGroup(loopHead);
- predIt = subloop;
-
- // Add all predecessors of the subloop header to the worklist, as long as
- // those predecessors are not in the current subloop. It might be the case
- // that they are in other loops, which we will then add as a subloop to the
- // current loop.
- for (BasicBlock *predIn : predIt->in)
- if (predIn->containingGroup() != subloop)
- worklist.push_back(predIn);
- } else {
- if (predIt == loopHead)
- continue;
-
- // This is an undiscovered block. Map it to the current loop.
- predIt->setContainingGroup(loopHead);
-
- // Add all incoming edges to the worklist.
- for (BasicBlock *bb : predIt->in)
- worklist.push_back(bb);
- }
- }
- }
-
-private:
- const DominatorTree &dt;
- QVector<LoopInfo *> loopInfos;
-
- void createLoopInfos(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- if (BasicBlock *loopHeader = bb->containingGroup())
- findLoop(loopHeader)->loopBody.append(bb);
- }
-
- for (int i = 0, size = loopInfos.size(); i < size; ++i) {
- if (BasicBlock *containingLoopHeader = loopInfos.at(i)->loopHeader->containingGroup())
- findLoop(containingLoopHeader)->addNestedLoop(loopInfos.at(i));
- }
- }
-
- LoopInfo *findLoop(BasicBlock *loopHeader)
- {
- for (LoopInfo *info : qAsConst(loopInfos)) {
- if (info->loopHeader == loopHeader)
- return info;
- }
-
- Q_UNREACHABLE();
- return nullptr;
- }
-};
-
-// High-level algorithm:
-// 0. start with the first node (the start node) of a function
-// 1. emit the node
-// 2. add all outgoing edges that are not yet emitted to the postponed stack
-// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
-// we're done.
-// 4. pop a node from the postponed stack, and check if it can be scheduled:
-// a. if all incoming edges are scheduled, go to 4.
-// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
-// back to the start of the loop), ignore it
-// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
-// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
-// 6. go back to 1.
-//
-// The postponing action in step 2 will put the node into its containing group. The case where this
-// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
-// outgoing edge points to a node that is not part of the current loop (and possibly not of the
-// parent loop).
-//
-// Linear scan register allocation benefits greatly from short life-time intervals with few holes
-// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
-// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
-// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
-// the same reason.
-class BlockScheduler
-{
- enum { DebugBlockScheduler = 0 };
-
- IR::Function *function;
- const DominatorTree &dominatorTree;
-
- struct WorkForGroup
- {
- BasicBlock *group;
- QStack<BasicBlock *> postponed;
-
- WorkForGroup(BasicBlock *group = 0): group(group) {}
- };
- WorkForGroup currentGroup;
- QStack<WorkForGroup> postponedGroups;
- QVector<BasicBlock *> sequence;
- ProcessedBlocks emitted;
- QHash<BasicBlock *, BasicBlock *> loopsStartEnd;
-
- bool checkCandidate(BasicBlock *candidate)
- {
- Q_ASSERT(candidate->containingGroup() == currentGroup.group);
-
- for (BasicBlock *in : candidate->in) {
- if (emitted.alreadyProcessed(in))
- continue;
-
- if (dominatorTree.dominates(candidate, in))
- // this is a loop, where there in -> candidate edge is the jump back to the top of the loop.
- continue;
-
- if (in == candidate)
- // this is a very tight loop, e.g.:
- // L1: ...
- // goto L1
- // This can happen when, for example, the basic-block merging gets rid of the empty
- // body block. In this case, we can safely schedule this block (if all other
- // incoming edges are either loop-back edges, or have been scheduled already).
- continue;
-
- return false; // an incoming edge that is not yet emitted, and is not a back-edge
- }
-
- if (candidate->isGroupStart()) {
- // postpone everything, and schedule the loop first.
- postponedGroups.push(currentGroup);
- currentGroup = WorkForGroup(candidate);
- }
-
- return true;
- }
-
- BasicBlock *pickNext()
- {
- while (true) {
- while (currentGroup.postponed.isEmpty()) {
- if (postponedGroups.isEmpty())
- return 0;
- if (currentGroup.group) // record the first and the last node of a group
- loopsStartEnd.insert(currentGroup.group, sequence.last());
- currentGroup = postponedGroups.pop();
- }
-
- BasicBlock *next = currentGroup.postponed.pop();
- if (checkCandidate(next))
- return next;
- }
-
- Q_UNREACHABLE();
- return 0;
- }
-
- void emitBlock(BasicBlock *bb)
- {
- Q_ASSERT(!bb->isRemoved());
- if (emitted.alreadyProcessed(bb))
- return;
-
- sequence.append(bb);
- emitted.markAsProcessed(bb);
- }
-
- void schedule(BasicBlock *functionEntryPoint)
- {
- BasicBlock *next = functionEntryPoint;
-
- while (next) {
- emitBlock(next);
- for (int i = next->out.size(); i != 0; ) {
- // postpone all outgoing edges, if they were not already processed
- --i;
- BasicBlock *out = next->out[i];
- if (!emitted.alreadyProcessed(out))
- postpone(out);
- }
- next = pickNext();
- }
- }
-
- void postpone(BasicBlock *bb)
- {
- if (currentGroup.group == bb->containingGroup()) {
- currentGroup.postponed.append(bb);
- return;
- }
-
- for (int i = postponedGroups.size(); i != 0; ) {
- --i;
- WorkForGroup &g = postponedGroups[i];
- if (g.group == bb->containingGroup()) {
- g.postponed.append(bb);
- return;
- }
- }
-
- Q_UNREACHABLE();
- }
-
- void dumpLoopStartsEnds() const
- {
- qDebug() << "Found" << loopsStartEnd.size() << "loops:";
- for (auto key : loopsStartEnd.keys())
- qDebug("Loop starting at L%d ends at L%d.", key->index(),
- loopsStartEnd.value(key)->index());
- }
-
-public:
- BlockScheduler(IR::Function *function, const DominatorTree &dominatorTree)
- : function(function)
- , dominatorTree(dominatorTree)
- , sequence(0)
- , emitted(function)
- {}
-
- QHash<BasicBlock *, BasicBlock *> go()
- {
- showMeTheCode(function, "Before block scheduling");
- if (DebugBlockScheduler)
- dominatorTree.dumpImmediateDominators();
-
- schedule(function->basicBlock(0));
-
- Q_ASSERT(function->liveBasicBlocksCount() == sequence.size());
- function->setScheduledBlocks(sequence);
- if (DebugBlockScheduler)
- dumpLoopStartsEnds();
- return loopsStartEnd;
- }
-};
-
-#ifndef QT_NO_DEBUG
-void checkCriticalEdges(const QVector<BasicBlock *> &basicBlocks) {
- for (BasicBlock *bb : basicBlocks) {
- if (bb && bb->out.size() > 1) {
- for (BasicBlock *bb2 : bb->out) {
- if (bb2 && bb2->in.size() > 1) {
- qDebug() << "found critical edge between block"
- << bb->index() << "and block" << bb2->index();
- Q_ASSERT(false);
- }
- }
- }
- }
-}
-#endif
-
-static void cleanupBasicBlocks(IR::Function *function)
-{
- showMeTheCode(function, "Before basic block cleanup");
-
- // Algorithm: this is the iterative version of a depth-first search for all blocks that are
- // reachable through outgoing edges, starting with the start block and all exception handler
- // blocks.
- QBitArray reachableBlocks(function->basicBlockCount());
- QVarLengthArray<BasicBlock *, 16> postponed;
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
- if (i == 0 || bb->isExceptionHandler())
- postponed.append(bb);
- }
-
- while (!postponed.isEmpty()) {
- BasicBlock *bb = postponed.back();
- postponed.pop_back();
- if (bb->isRemoved()) // this block was removed before, we don't need to clean it up.
- continue;
-
- reachableBlocks.setBit(bb->index());
-
- for (BasicBlock *outBB : bb->out) {
- if (!reachableBlocks.at(outBB->index()))
- postponed.append(outBB);
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) // the block has already been removed, so ignore it
- continue;
- if (reachableBlocks.at(bb->index())) // the block is reachable, so ignore it
- continue;
-
- for (BasicBlock *outBB : bb->out) {
- if (outBB->isRemoved() || !reachableBlocks.at(outBB->index()))
- continue; // We do not need to unlink from blocks that are scheduled to be removed.
-
- int idx = outBB->in.indexOf(bb);
- if (idx != -1) {
- outBB->in.remove(idx);
- for (Stmt *s : outBB->statements()) {
- if (Phi *phi = s->asPhi())
- phi->incoming.remove(idx);
- else
- break;
- }
- }
- }
-
- function->removeBasicBlock(bb);
- }
-
- showMeTheCode(function, "After basic block cleanup");
-}
-
-inline Const *isConstPhi(Phi *phi)
-{
- if (Const *c = phi->incoming[0]->asConst()) {
- for (int i = 1, ei = phi->incoming.size(); i != ei; ++i) {
- if (Const *cc = phi->incoming[i]->asConst()) {
- if (c->value != cc->value)
- return 0;
- if (!(c->type == cc->type || (c->type & NumberType && cc->type & NumberType)))
- return 0;
- if (int(c->value) == 0 && int(cc->value) == 0)
- if (isNegative(c->value) != isNegative(cc->value))
- return 0;
- } else {
- return 0;
- }
- }
- return c;
- }
- return 0;
-}
-
-static Expr *clone(Expr *e, IR::Function *function) {
- if (Temp *t = e->asTemp()) {
- return CloneExpr::cloneTemp(t, function);
- } else if (Const *c = e->asConst()) {
- return CloneExpr::cloneConst(c, function);
- } else if (Name *n = e->asName()) {
- return CloneExpr::cloneName(n, function);
- } else {
- Q_UNREACHABLE();
- return e;
- }
-}
-
-class ExprReplacer
-{
- DefUses &_defUses;
- IR::Function* _function;
- Temp *_toReplace;
- Expr *_replacement;
-
-public:
- ExprReplacer(DefUses &defUses, IR::Function *function)
- : _defUses(defUses)
- , _function(function)
- , _toReplace(0)
- , _replacement(0)
- {}
-
- bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
- {
- Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName());
-
- qSwap(_toReplace, toReplace);
- qSwap(_replacement, replacement);
-
- const QVector<Stmt *> &uses = _defUses.uses(*_toReplace);
-
- // Prevent the following:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %6
- // %6 = %1
- // From turning into:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %1
- //
- // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the
- // second phi node. So, if the defining statement for a temp is a phi node, and one of the
- // uses of the to-be-replaced statement is a phi node in the same block as the defining
- // statement, bail out.
- if (Temp *r = _replacement->asTemp()) {
- if (_defUses.defStmt(*r)->asPhi()) {
- BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r);
- for (Stmt *use : uses) {
- if (Phi *usePhi = use->asPhi()) {
- if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock)
- return false;
- }
- }
- }
- }
-
-// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
-
- if (newUses)
- newUses->reserve(uses.size());
-
-// qout << " " << uses.size() << " uses:"<<endl;
- for (Stmt *use : uses) {
-// qout<<" ";use->dump(qout);qout<<"\n";
- visit(use);
-// qout<<" -> ";use->dump(qout);qout<<"\n";
- W += use;
- if (newUses)
- newUses->push_back(use);
- }
-
- qSwap(_replacement, replacement);
- qSwap(_toReplace, toReplace);
- return true;
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *) {}
- void visitString(IR::String *) {}
- void visitRegExp(IR::RegExp *) {}
- void visitName(Name *) {}
- void visitTemp(Temp *) {}
- void visitArgLocal(ArgLocal *) {}
- void visitClosure(Closure *) {}
- void visitConvert(Convert *e) { check(e->expr); }
- void visitUnop(Unop *e) { check(e->expr); }
- void visitBinop(Binop *e) { check(e->left); check(e->right); }
- void visitCall(Call *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitNew(New *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- void visitMember(Member *e) { check(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { check(s->expr); }
- void visitMove(Move *s) { check(s->target); check(s->source); }
- void visitJump(Jump *) {}
- void visitCJump(CJump *s) { check(s->cond); }
- void visitRet(Ret *s) { check(s->expr); }
- void visitPhi(Phi *s) {
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- check(s->incoming[i]);
- }
-
-private:
- void check(Expr *&e) {
- if (equals(e, _toReplace)) {
- e = clone(_replacement, _function);
- } else {
- visit(e);
- }
- }
-
- // This only calculates equality for everything needed by constant propagation
- bool equals(Expr *e1, Expr *e2) const {
- if (e1 == e2)
- return true;
-
- if (Const *c1 = e1->asConst()) {
- if (Const *c2 = e2->asConst())
- return c1->value == c2->value && (c1->type == c2->type || (c1->type & NumberType && c2->type & NumberType));
- } else if (Temp *t1 = e1->asTemp()) {
- if (Temp *t2 = e2->asTemp())
- return *t1 == *t2;
- } else if (Name *n1 = e1->asName()) {
- if (Name *n2 = e2->asName()) {
- if (n1->id) {
- if (n2->id)
- return *n1->id == *n2->id;
- } else {
- return n1->builtin == n2->builtin;
- }
- }
- }
-
- if (e1->type == IR::NullType && e2->type == IR::NullType)
- return true;
- if (e1->type == IR::UndefinedType && e2->type == IR::UndefinedType)
- return true;
-
- return false;
- }
-};
-
-namespace {
-/// This function removes the basic-block from the function's list, unlinks any uses and/or defs,
-/// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them
-/// anymore.
-void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses,
- StatementWorklist &W, DominatorTree &dt)
-{
- enum { DebugUnlinking = 0 };
-
- struct Util {
- static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W)
- {
- int idx = to->in.indexOf(from);
- if (idx == -1)
- return;
-
- to->in.remove(idx);
- for (Stmt *outStmt : to->statements()) {
- if (!outStmt)
- continue;
- if (Phi *phi = outStmt->asPhi()) {
- if (Temp *t = phi->incoming[idx]->asTemp()) {
- defUses.removeUse(phi, *t);
- W += defUses.defStmt(*t);
- }
- phi->incoming.remove(idx);
- W += phi;
- } else {
- break;
- }
- }
- }
-
- static bool isReachable(BasicBlock *bb, const DominatorTree &dt)
- {
- for (BasicBlock *in : bb->in) {
- if (in->isRemoved())
- continue;
- if (dt.dominates(bb, in)) // a back-edge, not interesting
- continue;
- return true;
- }
-
- return false;
- }
- };
-
- Q_ASSERT(!from->isRemoved());
- Q_ASSERT(!to->isRemoved());
-
- // don't purge blocks that are entry points for catch statements. They might not be directly
- // connected, but are required anyway
- if (to->isExceptionHandler())
- return;
-
- if (DebugUnlinking)
- qDebug("Unlinking L%d -> L%d...", from->index(), to->index());
-
- // First, unlink the edge
- from->out.removeOne(to);
- Util::removeIncomingEdge(from, to, defUses, W);
-
- BasicBlockSet siblings;
- siblings.init(func);
-
- // Check if the target is still reachable...
- if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done.
- if (DebugUnlinking)
- qDebug(".. L%d is still reachable, recalulate idom.", to->index());
- dt.collectSiblings(to, siblings);
- } else {
- if (DebugUnlinking)
- qDebug(".. L%d is unreachable, purging it:", to->index());
- // The target is unreachable, so purge it:
- QVector<BasicBlock *> toPurge;
- toPurge.reserve(8);
- toPurge.append(to);
- while (!toPurge.isEmpty()) {
- BasicBlock *bb = toPurge.first();
- toPurge.removeFirst();
- if (DebugUnlinking)
- qDebug("... purging L%d", bb->index());
-
- if (bb->isRemoved())
- continue;
-
- // unlink all incoming edges
- for (BasicBlock *in : bb->in) {
- int idx = in->out.indexOf(bb);
- if (idx != -1)
- in->out.remove(idx);
- }
-
- // unlink all outgoing edges, including "arguments" to phi statements
- for (BasicBlock *out : bb->out) {
- if (out->isRemoved())
- continue;
-
- Util::removeIncomingEdge(bb, out, defUses, W);
-
- if (Util::isReachable(out, dt)) {
- dt.collectSiblings(out, siblings);
- } else {
- // if a successor has no incoming edges after unlinking the current basic block,
- // then it is unreachable, and can be purged too
- toPurge.append(out);
- }
- }
-
- // unlink all defs/uses from the statements in the basic block
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- W += defUses.removeDefUses(s);
- W -= s;
- }
-
- siblings.remove(bb);
- dt.setImmediateDominator(bb, 0);
- func->removeBasicBlock(bb);
- }
- }
-
- dt.recalculateIDoms(siblings);
- if (DebugUnlinking)
- qDebug("Unlinking done.");
-}
-
-bool tryOptimizingComparison(Expr *&expr)
-{
- Binop *b = expr->asBinop();
- if (!b)
- return false;
- Const *leftConst = b->left->asConst();
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- return false;
- Const *rightConst = b->right->asConst();
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- return false;
-
- QV4::Primitive l = convertToValue(leftConst);
- QV4::Primitive r = convertToValue(rightConst);
-
- switch (b->op) {
- case OpGt:
- leftConst->value = Runtime::method_compareGreaterThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLt:
- leftConst->value = Runtime::method_compareLessThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpGe:
- leftConst->value = Runtime::method_compareGreaterEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLe:
- leftConst->value = Runtime::method_compareLessEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictEqual:
- leftConst->value = Runtime::method_compareStrictEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpEqual:
- leftConst->value = Runtime::method_compareEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictNotEqual:
- leftConst->value = Runtime::method_compareStrictNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpNotEqual:
- leftConst->value = Runtime::method_compareNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QVector<LoopDetection::LoopInfo *>())
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (!showCode)
- return;
-
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- struct Util {
- QTextStream &qout;
- Util(QTextStream &qout): qout(qout) {}
- void genLoop(const LoopDetection::LoopInfo *loop)
- {
- qout << " subgraph \"cluster" << quint64(loop) << "\" {\n";
- qout << " L" << loop->loopHeader->index() << ";\n";
- for (BasicBlock *bb : loop->loopBody)
- qout << " L" << bb->index() << ";\n";
- for (LoopDetection::LoopInfo *nested : loop->nestedLoops)
- genLoop(nested);
- qout << " }\n";
- }
- };
-
- QString name;
- if (f->name) name = *f->name;
- else name = QStringLiteral("%1").arg((unsigned long long)f);
- qout << "digraph \"" << name << "\" { ordering=out;\n";
-
- for (LoopDetection::LoopInfo *l : loops) {
- if (l->parentLoop == 0)
- Util(qout).genLoop(l);
- }
-
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int idx = bb->index();
- qout << " L" << idx << " [label=\"L" << idx << "\"";
- if (idx == 0 || bb->terminator()->asRet())
- qout << ", shape=doublecircle";
- else
- qout << ", shape=circle";
- qout << "];\n";
- for (BasicBlock *out : bb->out)
- qout << " L" << idx << " -> L" << out->index() << "\n";
- }
-
- qout << "}\n";
- buf.close();
- qDebug("%s", buf.data().constData());
-}
-
-} // anonymous namespace
-
-void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
-{
- IR::Function *function = W.function();
- ExprReplacer replaceUses(defUses, function);
-
- Stmt *s = 0;
- while ((s = W.takeNext(s))) {
-
- if (Phi *phi = s->asPhi()) {
- // dead code elimination:
- if (defUses.useCount(*phi->targetTemp) == 0) {
- W += defUses.removeDefUses(phi);
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *c = isConstPhi(phi)) {
- replaceUses(phi->targetTemp, c, W);
- defUses.removeDef(*phi->targetTemp);
- W.remove(s);
- continue;
- }
-
- // copy propagation:
- if (phi->incoming.size() == 1) {
- Temp *t = phi->targetTemp;
- Expr *e = phi->incoming.first();
-
- QVector<Stmt *> newT2Uses;
- replaceUses(t, e, W, &newT2Uses);
- if (Temp *t2 = e->asTemp()) {
- defUses.removeUse(s, *t2);
- defUses.addUses(*t2, newT2Uses);
- W += defUses.defStmt(*t2);
- }
- defUses.removeDef(*t);
- W.remove(s);
- continue;
- }
- } else if (Move *m = s->asMove()) {
- if (Convert *convert = m->source->asConvert()) {
- if (Const *sourceConst = convert->expr->asConst()) {
- convertConst(sourceConst, convert->type);
- m->source = sourceConst;
- W += m;
- continue;
- } else if (Temp *sourceTemp = convert->expr->asTemp()) {
- if (sourceTemp->type == convert->type) {
- m->source = sourceTemp;
- W += m;
- continue;
- }
- }
- }
-
- if (Temp *targetTemp = m->target->asTemp()) {
- // dead code elimination:
- if (defUses.useCount(*targetTemp) == 0) {
- EliminateDeadCode(defUses, W).run(m->source, s);
- if (!m->source)
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *sourceConst = m->source->asConst()) {
- Q_ASSERT(sourceConst->type != UnknownType);
- replaceUses(targetTemp, sourceConst, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- continue;
- }
- if (Member *member = m->source->asMember()) {
- if (member->kind == Member::MemberOfEnum) {
- Const *c = function->New<Const>();
- const int enumValue = member->enumValue;
- c->init(SInt32Type, enumValue);
- replaceUses(targetTemp, c, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- defUses.removeUse(s, *member->base->asTemp());
- continue;
- } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) {
- // Attached properties have no dependency on their base. Isel doesn't
- // need it and we can eliminate the temp used to initialize it.
- defUses.removeUse(s, *member->base->asTemp());
- Const *c = function->New<Const>();
- c->init(SInt32Type, 0);
- member->base = c;
- continue;
- }
- }
-
- // copy propagation:
- if (Temp *sourceTemp = m->source->asTemp()) {
- QVector<Stmt *> newT2Uses;
- if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) {
- defUses.removeUse(s, *sourceTemp);
- defUses.addUses(*sourceTemp, newT2Uses);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- }
- continue;
- }
-
- if (Unop *unop = m->source->asUnop()) {
- // Constant unary expression evaluation:
- if (Const *constOperand = unop->expr->asConst()) {
- if (constOperand->type & NumberType || constOperand->type == BoolType) {
- // TODO: implement unop propagation for other constant types
- bool doneSomething = false;
- switch (unop->op) {
- case OpNot:
- constOperand->value = !constOperand->value;
- constOperand->type = BoolType;
- doneSomething = true;
- break;
- case OpUMinus:
- if (int(constOperand->value) == 0 && int(constOperand->value) == constOperand->value) {
- if (isNegative(constOperand->value))
- constOperand->value = 0;
- else
- constOperand->value = -1 / Q_INFINITY;
- constOperand->type = DoubleType;
- doneSomething = true;
- break;
- }
-
- constOperand->value = -constOperand->value;
- doneSomething = true;
- break;
- case OpUPlus:
- if (unop->type != UnknownType)
- constOperand->type = unop->type;
- doneSomething = true;
- break;
- case OpCompl:
- constOperand->value = ~QV4::Primitive::toInt32(constOperand->value);
- constOperand->type = SInt32Type;
- doneSomething = true;
- break;
- case OpIncrement:
- constOperand->value = constOperand->value + 1;
- doneSomething = true;
- break;
- case OpDecrement:
- constOperand->value = constOperand->value - 1;
- doneSomething = true;
- break;
- default:
- break;
- };
-
- if (doneSomething) {
- m->source = constOperand;
- W += m;
- }
- }
- }
- // TODO: if the result of a unary not operation is only used in a cjump,
- // then inline it.
-
- continue;
- }
-
- if (Binop *binop = m->source->asBinop()) {
- Const *leftConst = binop->left->asConst();
- Const *rightConst = binop->right->asConst();
-
- { // Typical casts to int32:
- Expr *casted = 0;
- switch (binop->op) {
- case OpBitAnd:
- if (leftConst && !rightConst && QV4::Primitive::toUInt32(leftConst->value) == 0xffffffff)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0xffffffff)
- casted = binop->left;
- break;
- case OpBitOr:
- if (leftConst && !rightConst && QV4::Primitive::toInt32(leftConst->value) == 0)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0)
- casted = binop->left;
- break;
- default:
- break;
- }
- if (casted && casted->type == SInt32Type) {
- m->source = casted;
- W += m;
- continue;
- }
- }
- if (rightConst) {
- switch (binop->op) {
- case OpLShift:
- case OpRShift:
- if (double v = QV4::Primitive::toInt32(rightConst->value) & 0x1f) {
- // mask right hand side of shift operations
- rightConst->value = v;
- rightConst->type = SInt32Type;
- } else {
- // shifting a value over 0 bits is a move:
- if (rightConst->value == 0) {
- m->source = binop->left;
- W += m;
- }
- }
-
- break;
- default:
- break;
- }
- }
-
- // TODO: More constant binary expression evaluation
- // TODO: If the result of the move is only used in one single cjump, then
- // inline the binop into the cjump.
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- continue;
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- continue;
-
- QV4::Primitive lc = convertToValue(leftConst);
- QV4::Primitive rc = convertToValue(rightConst);
- double l = lc.toNumber();
- double r = rc.toNumber();
-
- switch (binop->op) {
- case OpMul:
- leftConst->value = l * r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpAdd:
- leftConst->value = l + r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpSub:
- leftConst->value = l - r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpDiv:
- leftConst->value = l / r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpMod:
- leftConst->value = std::fmod(l, r);
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- default:
- if (tryOptimizingComparison(m->source))
- W += m;
- break;
- }
-
- continue;
- }
- } // TODO: var{#0} = double{%10} where %10 is defined once and used once. E.g.: function(t){t = t % 2; return t; }
-
- } else if (CJump *cjump = s->asCJump()) {
- if (Const *constantCondition = cjump->cond->asConst()) {
- // Note: this assumes that there are no critical edges! Meaning, we can safely purge
- // any basic blocks that are found to be unreachable.
- Jump *jump = function->NewStmt<Jump>();
- W.registerNewStatement(jump);
- if (convertToValue(constantCondition).toBoolean()) {
- jump->target = cjump->iftrue;
- unlink(cjump->parent, cjump->iffalse, function, defUses, W, df);
- } else {
- jump->target = cjump->iffalse;
- unlink(cjump->parent, cjump->iftrue, function, defUses, W, df);
- }
- W.replace(s, jump);
-
- continue;
- } else if (cjump->cond->asBinop()) {
- if (tryOptimizingComparison(cjump->cond))
- W += cjump;
- continue;
- }
- // TODO: Constant unary expression evaluation
- // TODO: if the expression is an unary not operation, lift the expression, and switch
- // the then/else blocks.
- }
- }
-
- W.applyToFunction();
-}
-
-//### TODO: use DefUses from the optimizer, because it already has all this information
-class InputOutputCollector
-{
- void setOutput(Temp *out)
- {
- Q_ASSERT(!output);
- output = out;
- }
-
-public:
- std::vector<Temp *> inputs;
- Temp *output;
-
- InputOutputCollector()
- { inputs.reserve(4); }
-
- void collect(Stmt *s) {
- inputs.resize(0);
- output = 0;
- visit(s);
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- inputs.push_back(t);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void visit(Stmt *s)
- {
- if (auto m = s->asMove()) {
- visit(m->source);
- if (Temp *t = m->target->asTemp()) {
- setOutput(t);
- } else {
- visit(m->target);
- }
- } else if (s->asPhi()) {
- // Handled separately
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-};
-
-/*
- * The algorithm is described in:
- *
- * Linear Scan Register Allocation on SSA Form
- * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010
- *
- * See LifeTimeIntervals::renumber for details on the numbering.
- */
-class LifeRanges {
- class LiveRegs
- {
- typedef std::vector<int> Storage;
- Storage regs;
-
- public:
- void insert(int r)
- {
- if (find(r) == end())
- regs.push_back(r);
- }
-
- void unite(const LiveRegs &other)
- {
- if (other.empty())
- return;
- if (empty()) {
- regs = other.regs;
- return;
- }
- for (int r : other.regs)
- insert(r);
- }
-
- typedef Storage::iterator iterator;
- iterator find(int r)
- { return std::find(regs.begin(), regs.end(), r); }
-
- iterator begin()
- { return regs.begin(); }
-
- iterator end()
- { return regs.end(); }
-
- void erase(iterator it)
- { regs.erase(it); }
-
- void remove(int r)
- {
- iterator it = find(r);
- if (it != end())
- erase(it);
- }
-
- bool empty() const
- { return regs.empty(); }
-
- int size() const
- { return int(regs.size()); }
-
- int at(int idx) const
- { return regs.at(idx); }
- };
-
- std::vector<LiveRegs> _liveIn;
- std::vector<LifeTimeInterval *> _intervals;
- LifeTimeIntervals::Ptr _sortedIntervals;
-
- LifeTimeInterval &interval(const Temp *temp)
- {
- LifeTimeInterval *lti = _intervals[temp->index];
- Q_ASSERT(lti);
- return *lti;
- }
-
- void ensureInterval(const IR::Temp &temp)
- {
- Q_ASSERT(!temp.isInvalid());
- LifeTimeInterval *&lti = _intervals[temp.index];
- if (lti)
- return;
- lti = new LifeTimeInterval;
- lti->setTemp(temp);
- }
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _sortedIntervals->positionForStatement(s);
- }
-
- int start(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->startPosition(bb);
- }
-
- int end(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->endPosition(bb);
- }
-
-public:
- LifeRanges(IR::Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops)
- : _intervals(function->tempCount)
- {
- _sortedIntervals = LifeTimeIntervals::create(function);
- _liveIn.resize(function->basicBlockCount());
-
- for (int i = function->basicBlockCount() - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- buildIntervals(bb, startEndLoops.value(bb, 0));
- }
-
- _intervals.clear();
- }
-
- LifeTimeIntervals::Ptr intervals() const { return _sortedIntervals; }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- qout << "Life ranges:" << endl;
- qout << "Intervals:" << endl;
- const auto intervals = _sortedIntervals->intervals();
- for (const LifeTimeInterval *range : intervals) {
- range->dump(qout);
- qout << endl;
- }
-
- IRPrinter printer(&qout);
- for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) {
- qout << "L" << i <<" live-in: ";
- auto live = _liveIn.at(i);
- if (live.empty())
- qout << "(none)";
- std::sort(live.begin(), live.end());
- for (int i = 0; i < live.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << '%' << live.at(i);
- }
- qout << endl;
- }
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
-private:
- void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd)
- {
- LiveRegs live;
- for (BasicBlock *successor : bb->out) {
- live.unite(_liveIn[successor->index()]);
- const int bbIndex = successor->in.indexOf(bb);
- Q_ASSERT(bbIndex >= 0);
-
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) {
- ensureInterval(*t);
- live.insert(t->index);
- }
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements = bb->statements();
-
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), end(bb));
-
- InputOutputCollector collector;
- for (int i = statements.size() - 1; i >= 0; --i) {
- Stmt *s = statements.at(i);
- if (Phi *phi = s->asPhi()) {
- ensureInterval(*phi->targetTemp);
- LiveRegs::iterator it = live.find(phi->targetTemp->index);
- if (it == live.end()) {
- // a phi node target that is only defined, but never used
- interval(phi->targetTemp).setFrom(start(bb));
- } else {
- live.erase(it);
- }
- _sortedIntervals->add(&interval(phi->targetTemp));
- continue;
- }
- collector.collect(s);
- //### TODO: use DefUses from the optimizer, because it already has all this information
- if (Temp *opd = collector.output) {
- ensureInterval(*opd);
- LifeTimeInterval &lti = interval(opd);
- lti.setFrom(defPosition(s));
- live.remove(lti.temp().index);
- _sortedIntervals->add(&lti);
- }
- //### TODO: use DefUses from the optimizer, because it already has all this information
- for (size_t i = 0, ei = collector.inputs.size(); i != ei; ++i) {
- Temp *opd = collector.inputs[i];
- ensureInterval(*opd);
- interval(opd).addRange(start(bb), usePosition(s));
- live.insert(opd->index);
- }
- }
-
- if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator()));
- }
-
- _liveIn[bb->index()] = std::move(live);
- }
-};
-
-void removeUnreachleBlocks(IR::Function *function)
-{
- QVector<BasicBlock *> newSchedule;
- newSchedule.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- newSchedule.append(bb);
- function->setScheduledBlocks(newSchedule);
-}
-
-class ConvertArgLocals
-{
-public:
- ConvertArgLocals(IR::Function *function)
- : function(function)
- , convertArgs(!function->usesArgumentsObject)
- {
- tempForFormal.resize(function->formals.size(), -1);
- tempForLocal.resize(function->locals.size(), -1);
- }
-
- void toTemps()
- {
- if (function->variablesCanEscape())
- return;
-
- QVector<Stmt *> extraMoves;
- if (convertArgs) {
- const int formalCount = function->formals.size();
- extraMoves.reserve(formalCount + function->basicBlock(0)->statementCount());
- extraMoves.resize(formalCount);
-
- for (int i = 0; i != formalCount; ++i) {
- const int newTemp = function->tempCount++;
- tempForFormal[i] = newTemp;
-
- ArgLocal *source = function->New<ArgLocal>();
- source->init(ArgLocal::Formal, i, 0);
-
- Temp *target = function->New<Temp>();
- target->init(Temp::VirtualRegister, newTemp);
-
- Move *m = function->NewStmt<Move>();
- m->init(target, source);
- extraMoves[i] = m;
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (!bb->isRemoved()) {
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- if (convertArgs && function->formals.size() > 0)
- function->basicBlock(0)->prependStatements(extraMoves);
-
- function->locals.clear();
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- check(e->expr);
- } else if (auto m = s->asMove()) {
- check(m->target); check(m->source);
- } else if (auto c = s->asCJump()) {
- check(c->cond);
- } else if (auto r = s->asRet()) {
- check(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- check(c->expr);
- } else if (auto u = e->asUnop()) {
- check(u->expr);
- } else if (auto b = e->asBinop()) {
- check(b->left); check(b->right);
- } else if (auto c = e->asCall()) {
- check(c->base);
- for (ExprList *it = c->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto n = e->asNew()) {
- check(n->base);
- for (ExprList *it = n->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- check(s->base); check(s->index);
- } else if (auto m = e->asMember()) {
- check(m->base);
- }
- }
-
- void check(Expr *&e) {
- if (ArgLocal *al = e->asArgLocal()) {
- if (al->kind == ArgLocal::Local) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForLocal(al->index));
- e = t;
- } else if (convertArgs && al->kind == ArgLocal::Formal) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForFormal(al->index));
- e = t;
- }
- } else {
- visit(e);
- }
- }
-
- int fetchTempForLocal(int local)
- {
- int &ref = tempForLocal[local];
- if (ref == -1)
- ref = function->tempCount++;
- return ref;
- }
-
- int fetchTempForFormal(int formal)
- {
- return tempForFormal[formal];
- }
-
- IR::Function *function;
- bool convertArgs;
- std::vector<int> tempForFormal;
- std::vector<int> tempForLocal;
-};
-
-class CloneBasicBlock: protected CloneExpr
-{
-public:
- BasicBlock *operator()(IR::BasicBlock *originalBlock)
- {
- block = new BasicBlock(originalBlock->function, 0);
-
- for (Stmt *s : originalBlock->statements()) {
- visit(s);
- clonedStmt->location = s->location;
- }
-
- return block;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- clonedStmt = block->EXP(clone(e->expr));
- } else if (auto m = s->asMove()) {
- clonedStmt = block->MOVE(clone(m->target), clone(m->source));
- } else if (auto j = s->asJump()) {
- clonedStmt = block->JUMP(j->target);
- } else if (auto c = s->asCJump()) {
- clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse);
- } else if (auto r = s->asRet()) {
- clonedStmt = block->RET(clone(r->expr));
- } else if (auto p = s->asPhi()) {
- Phi *phi = block->function->NewStmt<Phi>();
- clonedStmt = phi;
-
- phi->targetTemp = clone(p->targetTemp);
- for (Expr *in : p->incoming)
- phi->incoming.append(clone(in));
- block->appendStatement(phi);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private:
- IR::Stmt *clonedStmt;
-};
-
-static void verifyCFG(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) {
- Q_ASSERT(bb->in.isEmpty());
- Q_ASSERT(bb->out.isEmpty());
- continue;
- }
-
- Q_ASSERT(function->basicBlock(bb->index()) == bb);
-
- // Check the terminators:
- Stmt *terminator = bb->terminator();
- if (terminator == nullptr) {
- Stmt *last = bb->statements().last();
- Call *call = last->asExp()->expr->asCall();
- Name *baseName = call->base->asName();
- Q_ASSERT(baseName->builtin == Name::builtin_rethrow);
- Q_UNUSED(baseName);
- } else if (Jump *jump = terminator->asJump()) {
- Q_UNUSED(jump);
- Q_ASSERT(jump->target);
- Q_ASSERT(!jump->target->isRemoved());
- Q_ASSERT(bb->out.size() == 1);
- Q_ASSERT(bb->out.first() == jump->target);
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_UNUSED(cjump);
- Q_ASSERT(bb->out.size() == 2);
- Q_ASSERT(cjump->iftrue);
- Q_ASSERT(!cjump->iftrue->isRemoved());
- Q_ASSERT(cjump->iftrue == bb->out[0]);
- Q_ASSERT(cjump->iffalse);
- Q_ASSERT(!cjump->iffalse->isRemoved());
- Q_ASSERT(cjump->iffalse == bb->out[1]);
- } else if (terminator->asRet()) {
- Q_ASSERT(bb->out.size() == 0);
- } else {
- Q_UNREACHABLE();
- }
-
- // Check the outgoing edges:
- for (BasicBlock *out : bb->out) {
- Q_UNUSED(out);
- Q_ASSERT(!out->isRemoved());
- Q_ASSERT(out->in.contains(bb));
- }
-
- // Check the incoming edges:
- for (BasicBlock *in : bb->in) {
- Q_UNUSED(in);
- Q_ASSERT(!in->isRemoved());
- Q_ASSERT(in->out.contains(bb));
- }
- }
-}
-
-static void verifyImmediateDominators(const DominatorTree &dt, IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- cfg2dot(function);
- dt.dumpImmediateDominators();
- DominatorTree referenceTree(function);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- BasicBlock *idom = dt.immediateDominator(bb);
- BasicBlock *referenceIdom = referenceTree.immediateDominator(bb);
- Q_UNUSED(idom);
- Q_UNUSED(referenceIdom);
- Q_ASSERT(idom == referenceIdom);
- }
-}
-
-static void verifyNoPointerSharing(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- class {
- public:
- void operator()(IR::Function *f)
- {
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- private:
- void visit(Stmt *s)
- {
- check(s);
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- check(e);
- EXPR_VISIT_ALL_KINDS(e);
- }
-
- private:
- void check(Stmt *s)
- {
- Q_ASSERT(!stmts.contains(s));
- stmts.insert(s);
- }
-
- void check(Expr *e)
- {
- Q_ASSERT(!exprs.contains(e));
- exprs.insert(e);
- }
-
- QSet<Stmt *> stmts;
- QSet<Expr *> exprs;
- } V;
- V(function);
-}
-
-// Loop-peeling is done by unfolding the loop once. The "original" loop basic blocks stay where they
-// are, and a copy of the loop is placed after it. Special care is taken while copying the loop body:
-// by having the copies of the basic-blocks point to the same nodes as the "original" basic blocks,
-// updating the immediate dominators is easy: if the edge of a copied basic-block B points to a
-// block C that has also been copied, set the immediate dominator of B to the corresponding
-// immediate dominator of C. Finally, for any node outside the loop that gets a new edge attached,
-// the immediate dominator has to be re-calculated.
-class LoopPeeling
-{
- DominatorTree &dt;
-
-public:
- LoopPeeling(DominatorTree &dt)
- : dt(dt)
- {}
-
- void run(const QVector<LoopDetection::LoopInfo *> &loops)
- {
- for (LoopDetection::LoopInfo *loopInfo : loops)
- peelLoop(loopInfo);
- }
-
-private:
- // All copies have their outgoing edges pointing to the same successor block as the originals.
- // For each copied block, check where the outgoing edges point to. If it's a block inside the
- // (original) loop, rewire it to the corresponding copy. Otherwise, which is when it points
- // out of the loop, leave it alone.
- // As an extra, collect all edges that point out of the copied loop, because the targets need
- // to have their immediate dominator rechecked.
- void rewire(BasicBlock *newLoopBlock, const QVector<BasicBlock *> &from, const QVector<BasicBlock *> &to, QVector<BasicBlock *> &loopExits)
- {
- for (int i = 0, ei = newLoopBlock->out.size(); i != ei; ++i) {
- BasicBlock *&out = newLoopBlock->out[i];
- const int idx = from.indexOf(out);
- if (idx == -1) {
- if (!loopExits.contains(out))
- loopExits.append(out);
- } else {
- out->in.removeOne(newLoopBlock);
- BasicBlock *newTo = to.at(idx);
- newTo->in.append(newLoopBlock);
- out = newTo;
-
- Stmt *terminator = newLoopBlock->terminator();
- if (Jump *jump = terminator->asJump()) {
- Q_ASSERT(i == 0);
- jump->target = newTo;
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_ASSERT(i == 0 || i == 1);
- if (i == 0)
- cjump->iftrue = newTo;
- else
- cjump->iffalse = newTo;
- }
- }
- }
- }
-
- void peelLoop(LoopDetection::LoopInfo *loop)
- {
- IR::Function *f = loop->loopHeader->function;
- CloneBasicBlock clone;
-
- LoopDetection::LoopInfo unpeeled(*loop);
- unpeeled.loopHeader = clone(unpeeled.loopHeader);
- unpeeled.loopHeader->setContainingGroup(loop->loopHeader->containingGroup());
- unpeeled.loopHeader->markAsGroupStart(true);
- f->addBasicBlock(unpeeled.loopHeader);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *&bodyBlock = unpeeled.loopBody[i];
- bodyBlock = clone(bodyBlock);
- bodyBlock->setContainingGroup(unpeeled.loopHeader);
- Q_ASSERT(bodyBlock->statementCount() == loop->loopBody[i]->statementCount());
- }
-
- // The cloned blocks will have no incoming edges, but they do have outgoing ones (copying
- // the terminators will automatically insert that edge). The blocks where the originals
- // pointed to will have an extra incoming edge from the copied blocks.
-
- BasicBlock::IncomingEdges inCopy = loop->loopHeader->in;
- for (BasicBlock *in : inCopy) {
- if (loop->loopHeader != in // this can happen for really tight loops (where there are no body blocks). This is a back-edge in that case.
- && unpeeled.loopHeader != in && !unpeeled.loopBody.contains(in) // if the edge is not coming from within the copied set, leave it alone
- && !dt.dominates(loop->loopHeader, in)) // an edge coming from within the loop (so a back-edge): this is handled when rewiring all outgoing edges
- continue;
-
- unpeeled.loopHeader->in.append(in);
- loop->loopHeader->in.removeOne(in);
-
- Stmt *terminator = in->terminator();
- if (Jump *jump = terminator->asJump()) {
- jump->target = unpeeled.loopHeader;
- in->out[0] = unpeeled.loopHeader;
- } else if (CJump *cjump = terminator->asCJump()) {
- if (cjump->iftrue == loop->loopHeader) {
- cjump->iftrue = unpeeled.loopHeader;
- Q_ASSERT(in->out[0] == loop->loopHeader);
- in->out[0] = unpeeled.loopHeader;
- } else if (cjump->iffalse == loop->loopHeader) {
- cjump->iffalse = unpeeled.loopHeader;
- Q_ASSERT(in->out[1] == loop->loopHeader);
- in->out[1] = unpeeled.loopHeader;
- } else {
- Q_UNREACHABLE();
- }
- }
- }
-
- QVector<BasicBlock *> loopExits;
- loopExits.reserve(8);
- loopExits.append(unpeeled.loopHeader);
-
- rewire(unpeeled.loopHeader, loop->loopBody, unpeeled.loopBody, loopExits);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- rewire(bodyBlock, loop->loopBody, unpeeled.loopBody, loopExits);
- f->addBasicBlock(bodyBlock);
- }
-
- // The original loop is now peeled off, and won't jump back to the loop header. Meaning, it
- // is not a loop anymore, so unmark it.
- loop->loopHeader->markAsGroupStart(false);
- for (BasicBlock *bb : qAsConst(loop->loopBody))
- bb->setContainingGroup(loop->loopHeader->containingGroup());
-
- // Set the immediate dominator of the new loop header to the old one. The real immediate
- // dominator will be calculated later.
- dt.setImmediateDominator(unpeeled.loopHeader, loop->loopHeader);
- // calculate the idoms in a separate loop, because addBasicBlock in the previous loop will
- // set the block index, which in turn is used by the dominator tree.
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- BasicBlock *idom = dt.immediateDominator(loop->loopBody.at(i));
- const int idx = loop->loopBody.indexOf(idom);
- if (idom == loop->loopHeader)
- idom = unpeeled.loopHeader;
- else if (idx != -1)
- idom = unpeeled.loopBody.at(idx);
- Q_ASSERT(idom);
- dt.setImmediateDominator(bodyBlock, idom);
- }
-
- BasicBlockSet siblings(f);
- for (BasicBlock *bb : qAsConst(loopExits))
- dt.collectSiblings(bb, siblings);
-
- siblings.insert(unpeeled.loopHeader);
- dt.recalculateIDoms(siblings, loop->loopHeader);
- dt.dumpImmediateDominators();
- verifyImmediateDominators(dt, f);
- }
-};
-
-class RemoveLineNumbers: private SideEffectsChecker
-{
-public:
- static void run(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!hasSideEffects(s)) {
- s->location = QQmlJS::AST::SourceLocation();
- }
- }
- }
- }
-
-private:
- ~RemoveLineNumbers() {}
-
- static bool hasSideEffects(Stmt *stmt)
- {
- RemoveLineNumbers checker;
- if (auto e = stmt->asExp()) {
- checker.visit(e->expr);
- } else if (auto m = stmt->asMove()) {
- checker.visit(m->source);
- if (!checker.seenSideEffects()) {
- checker.visit(m->target);
- }
- } else if (auto c = stmt->asCJump()) {
- checker.visit(c->cond);
- } else if (auto r = stmt->asRet()) {
- checker.visit(r->expr);
- }
- return checker.seenSideEffects();
- }
-
- void visitTemp(Temp *) override final {}
-};
-
-void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt)
-{
- enum { DebugBlockMerging = 0 };
-
- if (function->hasTry)
- return;
-
- showMeTheCode(function, "Before basic block merging");
-
- // Now merge a basic block with its successor when there is one outgoing edge, and the
- // successor has one incoming edge.
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
-
- bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info
-
- if (bb->isRemoved()) continue; // the block has been removed, so ignore it
- if (bb->out.size() != 1) continue; // more than one outgoing edge
- BasicBlock *successor = bb->out.first();
- if (successor->in.size() != 1) continue; // more than one incoming edge
-
- // Loop header? No efficient way to update the other blocks that refer to this as containing group,
- // so don't do merging yet.
- if (successor->isGroupStart()) continue;
-
- // Ok, we can merge the two basic blocks.
- if (DebugBlockMerging) {
- qDebug("Merging L%d into L%d", successor->index(), bb->index());
- }
- Q_ASSERT(bb->terminator()->asJump());
- bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with:
- for (Stmt *s : successor->statements()) {
- bb->appendStatement(s); // add all statements from the successor to the current basic block
- if (auto cjump = s->asCJump())
- cjump->parent = bb;
- }
- bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator
- for (auto newSuccessor : bb->out) {
- for (auto &backlink : newSuccessor->in) {
- if (backlink == successor) {
- backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there.
- }
- }
- }
- if (du) {
- // all statements in successor have moved to bb, so make sure that the containing blocks
- // stored in DefUses get updated (meaning: point to bb)
- du->replaceBasicBlock(successor, bb);
- }
- if (dt) {
- // update the immediate dominators to: any block that was dominated by the successor
- // will now need to point to bb's immediate dominator. The reason is that bb itself
- // won't be anyones immediate dominator, because it had just one outgoing edge.
- dt->mergeIntoPredecessor(successor);
- }
- function->removeBasicBlock(successor);
- --i; // re-run on the current basic-block, so any chain gets collapsed.
- }
-
- showMeTheCode(function, "After basic block merging");
- verifyCFG(function);
-}
-
-} // anonymous namespace
-
-void LifeTimeInterval::setFrom(int from) {
- Q_ASSERT(from > 0);
-
- if (_ranges.isEmpty()) { // this is the case where there is no use, only a define
- _ranges.prepend(LifeTimeIntervalRange(from, from));
- if (_end == InvalidPosition)
- _end = from;
- } else {
- _ranges.first().start = from;
- }
-}
-
-void LifeTimeInterval::addRange(int from, int to) {
- Q_ASSERT(from > 0);
- Q_ASSERT(to > 0);
- Q_ASSERT(to >= from);
-
- if (_ranges.isEmpty()) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- _end = to;
- return;
- }
-
- LifeTimeIntervalRange *p = &_ranges.first();
- if (to + 1 >= p->start && p->end + 1 >= from) {
- p->start = qMin(p->start, from);
- p->end = qMax(p->end, to);
- while (_ranges.count() > 1) {
- LifeTimeIntervalRange *p1 = p + 1;
- if (p->end + 1 < p1->start || p1->end + 1 < p->start)
- break;
- p1->start = qMin(p->start, p1->start);
- p1->end = qMax(p->end, p1->end);
- _ranges.remove(0);
- p = &_ranges.first();
- }
- } else {
- if (to < p->start) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- } else {
- Q_ASSERT(from > _ranges.last().end);
- _ranges.push_back(LifeTimeIntervalRange(from, to));
- }
- }
-
- _end = _ranges.last().end;
-}
-
-LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
-{
- Q_ASSERT(atPosition < newStart || newStart == InvalidPosition);
- Q_ASSERT(atPosition <= _end);
- Q_ASSERT(newStart <= _end || newStart == InvalidPosition);
-
- if (_ranges.isEmpty() || atPosition < _ranges.first().start)
- return LifeTimeInterval();
-
- LifeTimeInterval newInterval = *this;
- newInterval.setSplitFromInterval(true);
-
- // search where to split the interval
- for (int i = 0, ei = _ranges.size(); i < ei; ++i) {
- if (_ranges.at(i).start <= atPosition) {
- if (_ranges.at(i).end >= atPosition) {
- // split happens in the middle of a range. Keep this range in both the old and the
- // new interval, and correct the end/start later
- _ranges.resize(i + 1);
- newInterval._ranges.remove(0, i);
- break;
- }
- } else {
- // split happens between two ranges.
- _ranges.resize(i);
- newInterval._ranges.remove(0, i);
- break;
- }
- }
-
- if (newInterval._ranges.first().end == atPosition)
- newInterval._ranges.remove(0);
-
- if (newStart == InvalidPosition) {
- // the temp stays inactive for the rest of its lifetime
- newInterval = LifeTimeInterval();
- } else {
- // find the first range where the temp will get active again:
- while (!newInterval._ranges.isEmpty()) {
- const LifeTimeIntervalRange &range = newInterval._ranges.first();
- if (range.start > newStart) {
- // The split position is before the start of the range. Either we managed to skip
- // over the correct range, or we got an invalid split request. Either way, this
- // Should Never Happen <TM>.
- Q_ASSERT(range.start > newStart);
- return LifeTimeInterval();
- } else if (range.start <= newStart && range.end >= newStart) {
- // yay, we found the range that should be the new first range in the new interval!
- break;
- } else {
- // the temp stays inactive for this interval, so remove it.
- newInterval._ranges.remove(0);
- }
- }
- Q_ASSERT(!newInterval._ranges.isEmpty());
- newInterval._ranges.first().start = newStart;
- _end = newStart;
- }
-
- // if we're in the middle of a range, set the end to the split position
- if (_ranges.last().end > atPosition)
- _ranges.last().end = atPosition;
-
- validate();
- newInterval.validate();
-
- return newInterval;
-}
-
-void LifeTimeInterval::dump(QTextStream &out) const {
- IRPrinter(&out).print(const_cast<Temp *>(&_temp));
- out << ": ends at " << _end << " with ranges ";
- if (_ranges.isEmpty())
- out << "(none)";
- for (int i = 0; i < _ranges.size(); ++i) {
- if (i > 0) out << ", ";
- out << _ranges[i].start << " - " << _ranges[i].end;
- }
- if (_reg != InvalidRegister)
- out << " (register " << _reg << ")";
-}
-
-
-bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- return r1->temp() < r2->temp();
-}
-
-LifeTimeIntervals::LifeTimeIntervals(IR::Function *function)
- : _basicBlockPosition(function->basicBlockCount())
- , _positionForStatement(function->statementCount(), IR::Stmt::InvalidId)
- , _lastPosition(0)
-{
- _intervals.reserve(function->tempCount + 32); // we reserve a bit more space for intervals, because the register allocator will add intervals with fixed ranges for each register.
- renumber(function);
-}
-
-// Renumbering works as follows:
-// - phi statements are not numbered
-// - statement numbers start at 0 (zero) and increment get an even number (lastPosition + 2)
-// - basic blocks start at firstStatementNumber - 1, or rephrased: lastPosition + 1
-// - basic blocks end at the number of the last statement
-// And during life-time calculation the next rule is used:
-// - any temporary starts its life-time at definingStatementPosition + 1
-//
-// This numbering simulates half-open intervals. For example:
-// 0: %1 = 1
-// 2: %2 = 2
-// 4: %3 = %1 + %2
-// 6: print(%3)
-// Here the half-open life-time intervals would be:
-// %1: (0-4]
-// %2: (2-4]
-// %3: (4-6]
-// Instead, we use the even statement positions for uses of temporaries, and the odd positions for
-// their definitions:
-// %1: [1-4]
-// %2: [3-4]
-// %3: [5-6]
-// This has the nice advantage that placing %3 (for example) is really easy: the start will
-// never overlap with the end of the uses of the operands used in the defining statement.
-//
-// The reason to start a basic-block at firstStatementPosition - 1 is to have correct start
-// positions for target temporaries of phi-nodes. Those temporaries will now start before the
-// first statement. This also means that any moves that get generated when transforming out of SSA
-// form, will not interfere with (read: overlap) any defining statements in the preceding
-// basic-block.
-void LifeTimeIntervals::renumber(IR::Function *function)
-{
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- _basicBlockPosition[bb->index()].start = _lastPosition + 1;
-
- for (Stmt *s : bb->statements()) {
- if (s->asPhi())
- continue;
-
- _lastPosition += 2;
- _positionForStatement[s->id()] = _lastPosition;
- }
-
- _basicBlockPosition[bb->index()].end = _lastPosition;
- }
-}
-
-LifeTimeIntervals::~LifeTimeIntervals()
-{
- qDeleteAll(_intervals);
-}
-
-Optimizer::Optimizer(IR::Function *function)
- : function(function)
- , inSSA(false)
-{}
-
-void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool peelLoops)
-{
- showMeTheCode(function, "Before running the optimizer");
-
- cleanupBasicBlocks(function);
-
- function->removeSharedExpressions();
- int statementCount = 0;
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- statementCount += bb->statementCount();
-// showMeTheCode(function);
-
- static bool doSSA = qEnvironmentVariableIsEmpty("QV4_NO_SSA");
-
- if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) {
-// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl;
-
- mergeBasicBlocks(function, nullptr, nullptr);
-
- ConvertArgLocals(function).toTemps();
- showMeTheCode(function, "After converting arguments to locals");
-
- // Calculate the dominator tree:
- DominatorTree df(function);
-
- {
- // This is in a separate scope, because loop-peeling doesn't (yet) update the LoopInfo
- // calculated by the LoopDetection. So by putting it in a separate scope, it is not
- // available after peeling.
-
- LoopDetection loopDetection(df);
- loopDetection.run(function);
- showMeTheCode(function, "After loop detection");
-// cfg2dot(function, loopDetection.allLoops());
-
- // ### disable loop peeling for now. It doesn't give any measurable performance
- // improvements at this time, but significantly increases the size of the
- // JIT generated code
- Q_UNUSED(peelLoops);
- if (0 && peelLoops) {
- QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops();
- LoopPeeling(df).run(innerLoops);
-
-// cfg2dot(function, loopDetection.allLoops());
- showMeTheCode(function, "After loop peeling");
- if (!innerLoops.isEmpty())
- verifyImmediateDominators(df, function);
- }
- }
-
- verifyCFG(function);
- verifyNoPointerSharing(function);
-
- df.computeDF();
-
- verifyCFG(function);
- verifyImmediateDominators(df, function);
-
- DefUses defUses(function);
-
-// qout << "Converting to SSA..." << endl;
- convertToSSA(function, df, defUses);
-// showMeTheCode(function);
-// defUses.dump();
-
-// qout << "Cleaning up phi nodes..." << endl;
- cleanupPhis(defUses);
- showMeTheCode(function, "After cleaning up phi-nodes");
-
- StatementWorklist worklist(function);
-
- if (doTypeInference) {
-// qout << "Running type inference..." << endl;
- TypeInference(qmlEngine, defUses).run(worklist);
- showMeTheCode(function, "After type inference");
-
-// qout << "Doing reverse inference..." << endl;
- ReverseInference(defUses).run(function);
-// showMeTheCode(function);
-
-// qout << "Doing type propagation..." << endl;
- TypePropagation(defUses).run(function, worklist);
-// showMeTheCode(function);
- verifyNoPointerSharing(function);
- }
-
- static const bool doOpt = qEnvironmentVariableIsEmpty("QV4_NO_OPT");
- if (doOpt) {
-// qout << "Running SSA optimization..." << endl;
- worklist.reset();
- optimizeSSA(worklist, defUses, df);
- showMeTheCode(function, "After optimization");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
- }
-
- verifyNoPointerSharing(function);
- mergeBasicBlocks(function, &defUses, &df);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the
- // condition is calculated to be always false) are not yet removed. This will choke the
- // block scheduling, so remove those now.
-// qout << "Cleaning up unreachable basic blocks..." << endl;
- cleanupBasicBlocks(function);
-// showMeTheCode(function);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Transform the CFG into edge-split SSA.
- showMeTheCode(function, "Before edge splitting");
- splitCriticalEdges(function, df, worklist, defUses);
- showMeTheCode(function, "After edge splitting");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
-// qout << "Doing block scheduling..." << endl;
-// df.dumpImmediateDominators();
- startEndLoops = BlockScheduler(function, df).go();
- showMeTheCode(function, "After basic block scheduling");
-// cfg2dot(function);
-
-#ifndef QT_NO_DEBUG
- checkCriticalEdges(function->basicBlocks());
-#endif
-
- if (!function->module->debugMode) {
- RemoveLineNumbers::run(function);
- showMeTheCode(function, "After line number removal");
- }
-
-// qout << "Finished SSA." << endl;
- inSSA = true;
- } else {
- removeUnreachleBlocks(function);
- inSSA = false;
- }
-}
-
-void Optimizer::convertOutOfSSA() {
- if (!inSSA)
- return;
-
- // There should be no critical edges at this point.
-
- for (BasicBlock *bb : function->basicBlocks()) {
- MoveMapping moves;
-
- for (BasicBlock *successor : bb->out) {
- const int inIdx = successor->in.indexOf(bb);
- Q_ASSERT(inIdx >= 0);
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- moves.add(clone(phi->incoming[inIdx], function),
- clone(phi->targetTemp, function)->asTemp());
- } else {
- break;
- }
- }
- }
-
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- os << "Move mapping for function ";
- if (function->name)
- os << *function->name;
- else
- os << (void *) function;
- os << " on basic-block L" << bb->index() << ":" << endl;
- moves.dump();
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
- moves.order();
-
- moves.insertMoves(bb, function, true);
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- while (!bb->isEmpty()) {
- if (bb->statements().first()->asPhi()) {
- bb->removeStatement(0);
- } else {
- break;
- }
- }
- }
-}
-
-LifeTimeIntervals::Ptr Optimizer::lifeTimeIntervals() const
-{
- Q_ASSERT(isInSSA());
-
- LifeRanges lifeRanges(function, startEndLoops);
-// lifeRanges.dump();
-// showMeTheCode(function);
- return lifeRanges.intervals();
-}
-
-static int countPhis(BasicBlock *bb)
-{
- int count = 0;
- for (Stmt *s : bb->statements()) {
- if (s->isa<Phi>())
- ++count;
- else
- break;
- }
-
- return count;
-}
-
-// Basic blocks can have only 1 terminator. This function returns a bit vector, where a 1 on a
-// certain index indicates that the terminator (jump) at the end of the basic block with that index
-// can be omitted.
-BitVector Optimizer::calculateOptionalJumps()
-{
- const int maxSize = function->basicBlockCount();
- BitVector optional(maxSize, false);
- if (maxSize < 2)
- return optional;
-
- BitVector reachableWithoutJump(maxSize, false);
-
- for (int i = maxSize - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- if (bb->isRemoved())
- continue;
-
- if (Jump *jump = bb->statements().last()->asJump()) {
- if (reachableWithoutJump.at(jump->target->index())) {
- if (bb->statements().size() - countPhis(bb)> 1)
- reachableWithoutJump.clear();
- optional.setBit(bb->index());
- reachableWithoutJump.setBit(bb->index());
- continue;
- }
- }
-
- reachableWithoutJump.clear();
- reachableWithoutJump.setBit(bb->index());
- }
-
- return optional;
-}
-
-void Optimizer::showMeTheCode(IR::Function *function, const char *marker)
-{
- ::showMeTheCode(function, marker);
-}
-
-static inline bool overlappingStorage(const Temp &t1, const Temp &t2)
-{
- // This is the same as the operator==, but for one detail: memory locations are not sensitive
- // to types, and neither are general-purpose registers.
-
- if (t1.index != t2.index)
- return false; // different position, where-ever that may (physically) be.
- if (t1.kind != t2.kind)
- return false; // formal/local/(physical-)register/stack do never overlap
- if (t1.kind != Temp::PhysicalRegister) // Other than registers, ...
- return t1.kind == t2.kind; // ... everything else overlaps: any memory location can hold everything.
-
- // So now the index is the same, and we know that both stored in a register. If both are
- // floating-point registers, they are the same. Or, if both are non-floating-point registers,
- // generally called general-purpose registers, they are also the same.
- return (t1.type == DoubleType && t2.type == DoubleType)
- || (t1.type != DoubleType && t2.type != DoubleType);
-}
-
-MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves)
-{
- Moves usages;
-
- if (Temp *t = e->asTemp()) {
- for (int i = 0, ei = moves.size(); i != ei; ++i) {
- const Move &move = moves[i];
- if (Temp *from = move.from->asTemp())
- if (overlappingStorage(*from, *t))
- usages.append(move);
- }
- }
-
- return usages;
-}
-
-void MoveMapping::add(Expr *from, Temp *to) {
- if (Temp *t = from->asTemp()) {
- if (overlappingStorage(*t, *to)) {
- // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped.
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Skipping ";
- printer.print(to);
- os << " <- ";
- printer.print(from);
- buf.close();
- qDebug("%s", buf.data().constData());
- }
- return;
- }
- }
-
- Move m(from, to);
- if (_moves.contains(m))
- return;
- _moves.append(m);
-}
-
-// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1]
-// section 6 for details). Now these moves form one or more graphs, so we have to output them in
-// such an order that values don't get overwritten:
-// r1 <- r0
-// r2 <- r1
-// That input has to be ordered as follows in order to prevent the value in r1 from being lost:
-// r2 <- r1
-// r1 <- r0
-//
-// So, the algorithm is to output the leaves first, and take them out of the input. This will result
-// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken
-// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until
-// there are no more leafs.
-//
-// The tricky part is that there might be cycles:
-// r4 <- r5
-// r5 <- r4
-// These have to be turned into a "register swap":
-// r4 <=> r5
-//
-// So after running the above algorithm where we progressively remove the leaves, we are left with
-// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other
-// edges we generate swaps. Note that the swaps will always occur as the last couple of moves,
-// because otherwise they might clobber sources for moves:
-// r4 <=> r5
-// r6 <- r5
-// Here, the value of r5 is already overwritten with the one in r4, so the correct order is:
-// r6 <- r5
-// r4 <=> r5
-void MoveMapping::order()
-{
- QList<Move> output;
- output.reserve(_moves.size());
-
- while (!_moves.isEmpty()) {
- // Take out all leaf edges, because we can output them without any problems.
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // No more leafs left, we're done here.
- output.append(_moves.takeAt(nextLeaf));
- // Now there might be new leaf edges: any move that had the input of the previously found
- // leaf as an output, so loop around.
- }
-
- while (!_moves.isEmpty()) {
- // We're now left with one or more cycles.
- // Step one: break the/a cycle.
- _moves.removeFirst();
- // Step two: find the other edges of the cycle, starting with the one of that is now a leaf.
- while (!_moves.isEmpty()) {
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // We're done with this cycle.
- Move m = _moves.takeAt(nextLeaf);
- // Step three: get the edges from the cycle and turn it into a swap
- m.needsSwap = true;
- output.append(m);
- // Because we took out a leaf, find the next one.
- }
- // We're done with the cycle, let's see if there are more.
- }
-
- _moves = output;
-}
-
-int MoveMapping::findLeaf() const
-{
- for (int i = 0, e = _moves.size(); i != e; ++i) {
- // Take an edge from the list...
- const Temp *target = _moves.at(i).to;
- // ... and see if its target is used as a source...
- bool targetUsedAsSource = false;
- for (int j = 0; j != e; ++j) {
- if (i == j)
- continue;
-
- Expr *source = _moves.at(j).from;
- if (const Temp *sourceTemp = source->asTemp()) {
- if (overlappingStorage(*target, *sourceTemp)) {
- targetUsedAsSource = true;
- break;
- }
- }
- }
- // ... if not, we have a leaf edge ...
- if (!targetUsedAsSource)
- return i;
- // .. otherwise we try the next one.
- }
-
- return -1; // No leaf found
-}
-
-QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const
-{
- QList<IR::Move *> newMoves;
- newMoves.reserve(_moves.size());
-
- int insertionPoint = atEnd ? bb->statements().size() - 1 : 0;
- for (const Move &m : _moves) {
- IR::Move *move = function->NewStmt<IR::Move>();
- move->init(clone(m.to, function), clone(m.from, function));
- move->swap = m.needsSwap;
- bb->insertStatementBefore(insertionPoint++, move);
- newMoves.append(move);
- }
-
- return newMoves;
-}
-
-void MoveMapping::dump() const
-{
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Move mapping has " << _moves.size() << " moves..." << endl;
- for (const Move &m : _moves) {
- os << "\t";
- printer.print(m.to);
- if (m.needsSwap)
- os << " <-> ";
- else
- os << " <-- ";
- printer.print(m.from);
- os << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-}
-
-// References:
-// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO'10, ACM Press, 2010
-// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
-// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132-141. ACM Press, 2005.
-// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the
-// Construction and Destruction of Static Single Assignment Form.
-// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge
-// University Press.
-// [Ramalingam] G. Ramalingam and T. Reps. An Incremental Algorithm for Maintaining the Dominator
-// Tree of a Reducible Flowgraph.
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
deleted file mode 100644
index b9d8ae0a6c..0000000000
--- a/src/qml/compiler/qv4ssa_p.h
+++ /dev/null
@@ -1,472 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4SSA_P_H
-#define QV4SSA_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qv4jsir_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4util_p.h>
-#include <QtCore/QSharedPointer>
-
-QT_BEGIN_NAMESPACE
-class QTextStream;
-class QQmlEnginePrivate;
-
-namespace QV4 {
-namespace IR {
-
-struct LifeTimeIntervalRange {
- int start;
- int end;
-
- LifeTimeIntervalRange(int start = -1, int end = -1)
- : start(start)
- , end(end)
- {}
-
- bool covers(int position) const { return start <= position && position <= end; }
-};
-} // IR namespace
-} // QV4 namespace
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE);
-
-namespace QV4 {
-namespace IR {
-
-class Q_AUTOTEST_EXPORT LifeTimeInterval {
-public:
- typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges;
-
-private:
- Temp _temp;
- Ranges _ranges;
- int _end;
- int _reg;
- unsigned _isFixedInterval : 1;
- unsigned _isSplitFromInterval : 1;
-
-public:
- enum { InvalidPosition = -1 };
- enum { InvalidRegister = -1 };
-
- explicit LifeTimeInterval(int rangeCapacity = 4)
- : _end(InvalidPosition)
- , _reg(InvalidRegister)
- , _isFixedInterval(0)
- , _isSplitFromInterval(0)
- { _ranges.reserve(rangeCapacity); }
-
- bool isValid() const { return _end != InvalidRegister; }
-
- void setTemp(const Temp &temp) { this->_temp = temp; }
- Temp temp() const { return _temp; }
- bool isFP() const { return _temp.type == IR::DoubleType; }
-
- void setFrom(int from);
- void addRange(int from, int to);
- const Ranges &ranges() const { return _ranges; }
-
- int start() const { return _ranges.first().start; }
- int end() const { return _end; }
- bool covers(int position) const
- {
- for (int i = 0, ei = _ranges.size(); i != ei; ++i) {
- if (_ranges.at(i).covers(position))
- return true;
- }
- return false;
- }
-
- int reg() const { return _reg; }
- void setReg(int reg) { Q_ASSERT(!_isFixedInterval); _reg = reg; }
-
- bool isFixedInterval() const { return _isFixedInterval; }
- void setFixedInterval(bool isFixedInterval) { _isFixedInterval = isFixedInterval; }
-
- LifeTimeInterval split(int atPosition, int newStart);
- bool isSplitFromInterval() const { return _isSplitFromInterval; }
- void setSplitFromInterval(bool isSplitFromInterval) { _isSplitFromInterval = isSplitFromInterval; }
-
- void dump(QTextStream &out) const;
- static bool lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
- static bool lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
-
- void validate() const {
-#if !defined(QT_NO_DEBUG)
- // Validate the new range
- if (_end != InvalidPosition) {
- Q_ASSERT(!_ranges.isEmpty());
- for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) {
- Q_ASSERT(range.start >= 0);
- Q_ASSERT(range.end >= 0);
- Q_ASSERT(range.start <= range.end);
- }
- }
-#endif
- }
-};
-
-inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- if (r1->_ranges.first().start == r2->_ranges.first().start) {
- if (r1->isSplitFromInterval() == r2->isSplitFromInterval())
- return r1->_ranges.last().end < r2->_ranges.last().end;
- else
- return r1->isSplitFromInterval();
- } else
- return r1->_ranges.first().start < r2->_ranges.first().start;
-}
-
-class LifeTimeIntervals
-{
- Q_DISABLE_COPY(LifeTimeIntervals)
-
- LifeTimeIntervals(IR::Function *function);
- void renumber(IR::Function *function);
-
-public:
- typedef QSharedPointer<LifeTimeIntervals> Ptr;
- static Ptr create(IR::Function *function)
- { return Ptr(new LifeTimeIntervals(function)); }
-
- ~LifeTimeIntervals();
-
- // takes ownership of the pointer
- void add(LifeTimeInterval *interval)
- { _intervals.append(interval); }
-
- // After calling Optimizer::lifeTimeIntervals() the result will have all intervals in descending order of start position.
- QVector<LifeTimeInterval *> intervals() const
- { return _intervals; }
-
- int size() const
- { return _intervals.size(); }
-
- int positionForStatement(Stmt *stmt) const
- {
- Q_ASSERT(stmt->id() >= 0);
- if (static_cast<unsigned>(stmt->id()) < _positionForStatement.size())
- return _positionForStatement[stmt->id()];
-
- return Stmt::InvalidId;
- }
-
- int startPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).start;
- }
-
- int endPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).end;
- }
-
- int lastPosition() const
- {
- return _lastPosition;
- }
-
-private:
- struct BasicBlockPositions {
- int start;
- int end;
-
- BasicBlockPositions()
- : start(IR::Stmt::InvalidId)
- , end(IR::Stmt::InvalidId)
- {}
- };
-
- std::vector<BasicBlockPositions> _basicBlockPosition;
- std::vector<int> _positionForStatement;
- QVector<LifeTimeInterval *> _intervals;
- int _lastPosition;
-};
-
-class Q_QML_PRIVATE_EXPORT Optimizer
-{
- Q_DISABLE_COPY(Optimizer)
-
-public:
- Optimizer(Function *function);
-
- void run(QQmlEnginePrivate *qmlEngine, bool doTypeInference = true, bool peelLoops = true);
- void convertOutOfSSA();
-
- bool isInSSA() const
- { return inSSA; }
-
- QHash<BasicBlock *, BasicBlock *> loopStartEndBlocks() const { return startEndLoops; }
-
- LifeTimeIntervals::Ptr lifeTimeIntervals() const;
-
- BitVector calculateOptionalJumps();
-
- static void showMeTheCode(Function *function, const char *marker);
-
-private:
- Function *function;
- bool inSSA;
- QHash<BasicBlock *, BasicBlock *> startEndLoops;
-};
-
-class Q_QML_AUTOTEST_EXPORT MoveMapping
-{
-#ifdef V4_AUTOTEST
-public:
-#endif
- struct Move {
- Expr *from;
- Temp *to;
- bool needsSwap;
-
- Move(Expr *from, Temp *to, bool needsSwap = false)
- : from(from), to(to), needsSwap(needsSwap)
- {}
-
- bool operator==(const Move &other) const
- { return from == other.from && to == other.to; }
- };
- typedef QList<Move> Moves;
-
- Moves _moves;
-
- static Moves sourceUsages(Expr *e, const Moves &moves);
-
-public:
- void add(Expr *from, Temp *to);
- void order();
- QList<IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const;
-
- void dump() const;
-
-private:
- int findLeaf() const;
-};
-
-/*
- * stack slot allocation:
- *
- * foreach bb do
- * foreach stmt do
- * if the current statement is not a phi-node:
- * purge ranges that end before the current statement
- * check for life ranges to activate, and if they don't have a stackslot associated then allocate one
- * renumber temps to stack
- * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated
- * if it's a jump:
- * foreach phi node in the successor:
- * allocate slots for each temp (both sources and targets) if they don't have one allocated already
- * insert moves before the jump
- */
-class AllocateStackSlots: protected ConvertTemps
-{
- IR::LifeTimeIntervals::Ptr _intervals;
- QVector<IR::LifeTimeInterval *> _unhandled;
- QVector<IR::LifeTimeInterval *> _live;
- QBitArray _slotIsInUse;
- IR::Function *_function;
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _intervals->positionForStatement(s);
- }
-
-public:
- AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals)
- : _intervals(intervals)
- , _slotIsInUse(intervals->size(), false)
- , _function(0)
- {
- _live.reserve(8);
- _unhandled = _intervals->intervals();
- }
-
- void forFunction(IR::Function *function)
- {
- IR::Optimizer::showMeTheCode(function, "Before stack slot allocation");
- _function = function;
- toStackSlots(function);
- }
-
-protected:
- int allocateFreeSlot() override
- {
- for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) {
- if (!_slotIsInUse[i]) {
- if (_nextUnusedStackSlot <= i) {
- Q_ASSERT(_nextUnusedStackSlot == i);
- _nextUnusedStackSlot = i + 1;
- }
- _slotIsInUse[i] = true;
- return i;
- }
- }
-
- Q_UNREACHABLE();
- return -1;
- }
-
- void process(IR::Stmt *s) override
- {
-// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id);
-
- if (IR::Phi *phi = s->asPhi()) {
- visitPhi(phi);
- } else {
- // purge ranges no longer alive:
- for (int i = 0; i < _live.size(); ) {
- const IR::LifeTimeInterval *lti = _live.at(i);
- if (lti->end() < usePosition(s)) {
-// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index];
- _live.remove(i);
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]);
- _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false;
- continue;
- } else {
- ++i;
- }
- }
-
- // active new ranges:
- while (!_unhandled.isEmpty()) {
- IR::LifeTimeInterval *lti = _unhandled.last();
- if (lti->start() > defPosition(s))
- break; // we're done
- Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index));
- _stackSlotForTemp[lti->temp().index] = allocateFreeSlot();
-// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index];
- _live.append(lti);
- _unhandled.removeLast();
- }
-
- visit(s);
- }
-
- if (IR::Jump *jump = s->asJump()) {
- IR::MoveMapping moves;
- for (IR::Stmt *succStmt : jump->target->statements()) {
- if (IR::Phi *phi = succStmt->asPhi()) {
- forceActivation(*phi->targetTemp);
- for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) {
- IR::Expr *e = phi->incoming[i];
- if (IR::Temp *t = e->asTemp()) {
- forceActivation(*t);
- }
- if (jump->target->in[i] == _currentBasicBlock)
- moves.add(phi->incoming[i], phi->targetTemp);
- }
- } else {
- break;
- }
- }
- moves.order();
- const QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
- for (IR::Move *move : newMoves)
- visit(move);
- }
- }
-
- void forceActivation(const IR::Temp &t)
- {
- if (_stackSlotForTemp.contains(t.index))
- return;
-
- int i = _unhandled.size() - 1;
- for (; i >= 0; --i) {
- IR::LifeTimeInterval *lti = _unhandled[i];
- if (lti->temp() == t) {
- _live.append(lti);
- _unhandled.remove(i);
- break;
- }
- }
- Q_ASSERT(i >= 0); // check that we always found the entry
-
- _stackSlotForTemp[t.index] = allocateFreeSlot();
-// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index];
- }
-
- void visitPhi(IR::Phi *phi) override
- {
- Q_UNUSED(phi);
-#if !defined(QT_NO_DEBUG)
- Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index));
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]);
- for (IR::Expr *e : phi->incoming) {
- if (IR::Temp *t = e->asTemp())
- Q_ASSERT(_stackSlotForTemp.contains(t->index));
- }
-#endif // defined(QT_NO_DEBUG)
- }
-};
-
-} // IR namespace
-} // QV4 namespace
-
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QV4SSA_P_H
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 9ffe6ff247..f17b1a7528 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -151,74 +151,120 @@ class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDef
Q_OBJECT
public:
- class FunctionRefCount : public QQmlRefCount {
- public:
- FunctionRefCount(QV4::Function *function):
- m_function(function)
+ struct Location {
+ Location(const QQmlSourceLocation &location = QQmlSourceLocation(),
+ const QUrl &url = QUrl()) :
+ location(location), url(url) {}
+ QQmlSourceLocation location;
+ QUrl url;
+ };
+
+ // Unfortunately we have to resolve the locations right away because the QML context might not
+ // be available anymore when we send the data.
+ struct RefLocation : public Location {
+ RefLocation()
+ : Location(), locationType(MaximumRangeType), sent(false)
{
- m_function->compilationUnit->addref();
+ function = nullptr;
}
- FunctionRefCount(const FunctionRefCount &other) :
- QQmlRefCount(other), m_function(other.m_function)
+ RefLocation(QV4::Function *ref)
+ : Location(ref->sourceLocation()), locationType(Binding), sent(false)
{
- m_function->compilationUnit->addref();
+ function = ref;
+ function->compilationUnit->addref();
}
- FunctionRefCount &operator=(const FunctionRefCount &other)
+ RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type)
+ : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
+ locationType(Creating), sent(false)
+ {
+ unit = ref;
+ unit->addref();
+ }
+
+ RefLocation(QQmlBoundSignalExpression *ref)
+ : Location(ref->sourceLocation()), locationType(HandlingSignal), sent(false)
+ {
+ boundSignal = ref;
+ boundSignal->addref();
+ }
+
+ RefLocation(QQmlDataBlob *ref)
+ : Location(QQmlSourceLocation()), locationType(Compiling), sent(false)
+ {
+ blob = ref;
+ blob->addref();
+ }
+
+ RefLocation(const RefLocation &other)
+ : Location(other),
+ locationType(other.locationType),
+ function(other.function),
+ sent(other.sent)
+ {
+ addref();
+ }
+
+ RefLocation &operator=(const RefLocation &other)
{
if (this != &other) {
- QQmlRefCount::operator=(other);
- other.m_function->compilationUnit->addref();
- m_function->compilationUnit->release();
- m_function = other.m_function;
+ release();
+ Location::operator=(other);
+ locationType = other.locationType;
+ function = other.function;
+ sent = other.sent;
+ addref();
}
return *this;
}
- ~FunctionRefCount()
+ ~RefLocation()
{
- m_function->compilationUnit->release();
+ release();
}
- private:
- QV4::Function *m_function;
- };
-
- struct Location {
- Location(const QQmlSourceLocation &location = QQmlSourceLocation(),
- const QUrl &url = QUrl()) :
- location(location), url(url) {}
- QQmlSourceLocation location;
- QUrl url;
- };
+ void addref()
+ {
+ switch (locationType) {
+ case Binding:
+ function->compilationUnit->addref();
+ break;
+ case Creating:
+ unit->addref();
+ break;
+ case HandlingSignal:
+ boundSignal->addref();
+ break;
+ case Compiling:
+ blob->addref();
+ break;
+ default:
+ Q_ASSERT(locationType == MaximumRangeType);
+ break;
+ }
+ }
- // Unfortunately we have to resolve the locations right away because the QML context might not
- // be available anymore when we send the data.
- struct RefLocation : public Location {
- RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr), sent(false)
- {}
-
- RefLocation(QV4::Function *function) :
- Location(function->sourceLocation()), locationType(Binding),
- ref(new FunctionRefCount(function),
- QQmlRefPointer<QQmlRefCount>::Adopt), sent(false)
- {}
-
- RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj,
- const QString &type) :
- Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
- locationType(Creating), ref(ref), sent(false)
- {}
-
- RefLocation(QQmlBoundSignalExpression *ref) :
- Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref), sent(false)
- {}
-
- RefLocation(QQmlDataBlob *ref) :
- Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref),
- sent(false)
- {}
+ void release()
+ {
+ switch (locationType) {
+ case Binding:
+ function->compilationUnit->release();
+ break;
+ case Creating:
+ unit->release();
+ break;
+ case HandlingSignal:
+ boundSignal->release();
+ break;
+ case Compiling:
+ blob->release();
+ break;
+ default:
+ Q_ASSERT(locationType == MaximumRangeType);
+ break;
+ }
+ }
bool isValid() const
{
@@ -226,7 +272,12 @@ public:
}
RangeType locationType;
- QQmlRefPointer<QQmlRefCount> ref;
+ union {
+ QV4::Function *function;
+ QV4::CompiledData::CompilationUnit *unit;
+ QQmlBoundSignalExpression *boundSignal;
+ QQmlDataBlob *blob;
+ };
bool sent;
};
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 7ea4e951d5..2080844d93 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -1,22 +1,10 @@
-include(../../3rdparty/masm/masm-defs.pri)
-
INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
-HEADERS += \
- $$PWD/qv4assembler_p.h \
- $$PWD/qv4regalloc_p.h \
- $$PWD/qv4targetplatform_p.h \
- $$PWD/qv4isel_masm_p.h \
- $$PWD/qv4binop_p.h \
- $$PWD/qv4unop_p.h \
- $$PWD/qv4registerinfo_p.h
-
SOURCES += \
- $$PWD/qv4assembler.cpp \
- $$PWD/qv4regalloc.cpp \
- $$PWD/qv4isel_masm.cpp \
- $$PWD/qv4binop.cpp \
- $$PWD/qv4unop.cpp \
+ $$PWD/qv4jit.cpp \
+ $$PWD/qv4assembler.cpp
-include(../../3rdparty/masm/masm.pri)
+HEADERS += \
+ $$PWD/qv4jit_p.h \
+ $$PWD/qv4assembler_p.h
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index d062f3bbb2..e357211074 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -37,510 +37,1105 @@
**
****************************************************************************/
-#include "qv4isel_masm_p.h"
-#include "qv4runtime_p.h"
-#include "qv4ssa_p.h"
-#include "qv4regalloc_p.h"
+#include <QBuffer>
+
+#include "qv4engine_p.h"
#include "qv4assembler_p.h"
+#include <private/qv4function_p.h>
+#include <private/qv4runtime_p.h>
+#include <wtf/Vector.h>
+#include <assembler/MacroAssembler.h>
+#include <assembler/MacroAssemblerCodeRef.h>
#include <assembler/LinkBuffer.h>
#include <WTFStubs.h>
-#if !defined(V4_BOOTSTRAP)
-#include "qv4function_p.h"
+#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
+
+#ifdef Q_STATIC_ASSERT_FOR_SANE_COMPILERS
+# undef Q_STATIC_ASSERT_FOR_SANE_COMPILERS
+#endif
+#if defined(Q_CC_MSVC) && _MSC_VER < 1900
+# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) // insane
+#else
+# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) Q_STATIC_ASSERT(x)
#endif
-#include <iostream>
-#include <QBuffer>
-#include <QCoreApplication>
+#ifdef V4_ENABLE_JIT
-#if ENABLE(ASSEMBLER)
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
-#if USE(UDIS86)
-# include <udis86.h>
-#endif
+#define callHelper(x) PlatformAssemblerCommon::callRuntime(#x, reinterpret_cast<void *>(&x))
-using namespace QV4;
-using namespace QV4::JIT;
+const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
-CompilationUnit::~CompilationUnit()
+static ReturnedValue toNumberHelper(ReturnedValue v)
{
+ return Encode(Value::fromReturnedValue(v).toNumber());
}
-#if !defined(V4_BOOTSTRAP)
+static ReturnedValue toInt32Helper(ReturnedValue v)
+{
+ return Encode(Value::fromReturnedValue(v).toInt32());
+}
-void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
+#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN)
+
+struct PlatformAssembler_X86_64_SysV : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
{
- runtimeFunctions.resize(data->functionTableSize);
- runtimeFunctions.fill(0);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg5Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = RegisterID::edi;
+ static const RegisterID Arg1Reg = RegisterID::esi;
+ static const RegisterID Arg2Reg = RegisterID::edx;
+ static const RegisterID Arg3Reg = RegisterID::ecx;
+ static const RegisterID Arg4Reg = RegisterID::r8;
+ static const RegisterID Arg5Reg = RegisterID::r9;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 6;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
- QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction,
- (ReturnedValue (*)(QV4::ExecutionEngine *, const uchar *)) codeRefs[i].code().executableAddress());
- runtimeFunctions[i] = runtimeFunction;
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
}
-}
-bool CompilationUnit::memoryMapCode(QString *errorString)
-{
- Q_UNUSED(errorString);
- codeRefs.resize(data->functionTableSize);
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
- const char *basePtr = reinterpret_cast<const char *>(data);
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
- for (uint i = 0; i < data->functionTableSize; ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
- void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset));
- JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr));
- JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize);
- codeRefs[i] = codeRef;
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
- if (showCode) {
- WTF::dataLogF("Mapped JIT code for %s\n", qPrintable(stringAt(compiledFunction->nameIndex)));
- disassemble(codeRef.code(), compiledFunction->codeSize, " ", WTF::dataFile());
- }
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
}
+};
- return true;
-}
+typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase;
-#endif // !defined(V4_BOOTSTRAP)
+#endif
+#if defined(Q_OS_WIN)
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
+struct PlatformAssembler_Win64 : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
{
- const int codeAlignment = 16;
- quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
- for (int i = 0; i < codeRefs.size(); ++i) {
- CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
- compiledFunction->codeOffset = offset;
- compiledFunction->codeSize = codeRefs.at(i).size();
- offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg3Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = RegisterID::ecx;
+ static const RegisterID Arg1Reg = RegisterID::edx;
+ static const RegisterID Arg2Reg = RegisterID::r8;
+ static const RegisterID Arg3Reg = RegisterID::r9;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
}
-}
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ subPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ call(ScratchRegister);
+ addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_Win64 PlatformAssemblerBase;
+
+#endif
+#endif
+
+#if (defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_X86_64)) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+struct PlatformAssembler_X86_All : JSC::MacroAssembler<JSC::MacroAssemblerX86>
{
- Q_ASSERT(device->pos() == unit->unitSize);
- Q_ASSERT(device->atEnd());
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = RegisterID::eax;
+ static const RegisterID ReturnValueRegisterTag = RegisterID::edx;
+ static const RegisterID ScratchRegister = RegisterID::ecx;
+ static const RegisterID AccumulatorRegisterValue = ReturnValueRegisterValue;
+ static const RegisterID AccumulatorRegisterTag = ReturnValueRegisterTag;
+ static const RegisterID JSStackFrameRegister = RegisterID::ebx;
+ static const RegisterID CppStackFrameRegister = RegisterID::esi;
+ static const RegisterID EngineRegister = RegisterID::edi;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = NoRegister;
+ static const RegisterID Arg1Reg = NoRegister;
+ static const RegisterID Arg2Reg = NoRegister;
+ static const RegisterID Arg3Reg = NoRegister;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 0;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
- QByteArray padding;
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegisterValue); push(AccumulatorRegisterValue); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister);
+ loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
- for (int i = 0; i < codeRefs.size(); ++i) {
- const CompiledData::Function *compiledFunction = unit->functionAt(i);
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
- if (device->pos() > qint64(compiledFunction->codeOffset)) {
- *errorString = QStringLiteral("Invalid state of cache file to write.");
- return false;
- }
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
- const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
- padding.fill(0, paddingSize);
- qint64 written = device->write(padding);
- if (written != padding.size()) {
- *errorString = device->errorString();
- return false;
- }
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
- const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation();
- written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize);
- if (written != qint64(compiledFunction->codeSize)) {
- *errorString = device->errorString();
- return false;
- }
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
}
- return true;
-}
+};
-template <typename TargetConfiguration>
-Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator)
- : _function(function)
- , _nextBlock(0)
- , _executableAllocator(executableAllocator)
- , _jsGenerator(jsGenerator)
-{
- _addrs.resize(_function->basicBlockCount());
- _patches.resize(_function->basicBlockCount());
- _labelPatches.resize(_function->basicBlockCount());
-}
+typedef PlatformAssembler_X86_All PlatformAssemblerBase;
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock)
-{
- _addrs[block->index()] = label();
- catchBlock = block->catchBlock;
- _nextBlock = nextBlock;
-}
+#endif
+
+#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target)
+struct PlatformAssembler_ARM64 : JSC::MacroAssembler<JSC::MacroAssemblerARM64>
{
- Q_UNUSED(current);
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0;
+ static const RegisterID AccumulatorRegister = JSC::ARM64Registers::x9;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = JSC::ARM64Registers::x10;
+ static const RegisterID ScratchRegister2 = JSC::ARM64Registers::x7; // Note: overlaps with Arg7Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = JSC::ARM64Registers::x19;
+ static const RegisterID CppStackFrameRegister = JSC::ARM64Registers::x20;
+ static const RegisterID EngineRegister = JSC::ARM64Registers::x21;
+ static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp;
+ static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp;
+ static const FPRegisterID FPScratchRegister = JSC::ARM64Registers::q1;
+
+ static const RegisterID Arg0Reg = JSC::ARM64Registers::x0;
+ static const RegisterID Arg1Reg = JSC::ARM64Registers::x1;
+ static const RegisterID Arg2Reg = JSC::ARM64Registers::x2;
+ static const RegisterID Arg3Reg = JSC::ARM64Registers::x3;
+ static const RegisterID Arg4Reg = JSC::ARM64Registers::x4;
+ static const RegisterID Arg5Reg = JSC::ARM64Registers::x5;
+ static const RegisterID Arg6Reg = JSC::ARM64Registers::x6;
+ static const RegisterID Arg7Reg = JSC::ARM64Registers::x7;
+ static const int ArgInRegCount = 8;
+
+ void push(RegisterID src)
+ {
+ pushToSave(src);
+ }
- if (target != _nextBlock)
- _patches[target->index()].push_back(jump());
-}
+ void pop(RegisterID dest)
+ {
+ popToRestore(dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump)
-{
- _patches[targetBlock->index()].push_back(targetJump);
-}
+ void pop()
+ {
+ add64(TrustedImm32(16), stackPointerRegister);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target)
-{
- DataLabelPatch p;
- p.dataLabel = patch;
- p.target = target;
- _dataLabelPatches.push_back(p);
-}
+ void popValue()
+ {
+ pop();
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target)
-{
- _labelPatches[target->index()].push_back(patch);
-}
+ void generatePlatformFunctionEntry()
+ {
+ pushPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ move(RegisterID::sp, RegisterID::fp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); // exceptionHandler
+ pushPair(JSStackFrameRegister, AccumulatorRegister);
+ pushPair(EngineRegister, CppStackFrameRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock)
-{
- generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock);
-}
+ void generatePlatformFunctionExit()
+ {
+ move(AccumulatorRegister, ReturnValueRegister);
+ popPair(EngineRegister, CppStackFrameRegister);
+ popPair(JSStackFrameRegister, AccumulatorRegister);
+ popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ ret();
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond,
- RegisterID left,
- TrustedImm32 right,
- IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
}
-}
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond,
- RegisterID left,
- RegisterID right,
- IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
+ void pushAligned(RegisterID reg)
+ {
+ pushToSave(reg);
}
-}
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer
-Assembler<TargetConfiguration>::loadAddressForWriting(RegisterID tmp, IR::Expr *e, WriteBarrier::Type *barrier)
-{
- if (barrier)
- *barrier = WriteBarrier::NoBarrier;
- IR::Temp *t = e->asTemp();
- if (t)
- return loadTempAddress(t);
- else
- return loadArgLocalAddressForWriting(tmp, e->asArgLocal(), barrier);
-}
+ void popAligned(RegisterID reg)
+ {
+ popToRestore(reg);
+ }
+};
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t)
+typedef PlatformAssembler_ARM64 PlatformAssemblerBase;
+
+#endif
+
+#if defined(Q_PROCESSOR_ARM_32) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+struct PlatformAssembler_ARM32 : JSC::MacroAssembler<JSC::MacroAssemblerARMv7>
{
- if (t->kind == IR::Temp::StackSlot)
- return stackSlotPointer(t);
- else
- Q_UNREACHABLE();
-}
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = JSC::ARMRegisters::r0;
+ static const RegisterID ReturnValueRegisterTag = JSC::ARMRegisters::r1;
+ static const RegisterID ScratchRegister = JSC::ARMRegisters::r2;
+ static const RegisterID AccumulatorRegisterValue = JSC::ARMRegisters::r4;
+ static const RegisterID AccumulatorRegisterTag = JSC::ARMRegisters::r5;
+ // r6 is used by MacroAssemblerARMv7
+ static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8;
+ static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10;
+#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r11;
+#else // Thumbs down
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r7;
+#endif
+ static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13;
+ static const FPRegisterID FPScratchRegister = JSC::ARMRegisters::d1;
+
+ static const RegisterID Arg0Reg = JSC::ARMRegisters::r0;
+ static const RegisterID Arg1Reg = JSC::ARMRegisters::r1;
+ static const RegisterID Arg2Reg = JSC::ARMRegisters::r2;
+ static const RegisterID Arg3Reg = JSC::ARMRegisters::r3;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImm32(sizeof(ReturnedValue)), StackPointerRegister);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer
-Assembler<TargetConfiguration>::loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier)
+ void generatePlatformFunctionEntry()
+ {
+ push(JSC::ARMRegisters::lr);
+ push(FramePointerRegister);
+ move(StackPointerRegister, FramePointerRegister);
+ push(TrustedImm32(0)); // exceptionHandler
+ push(AccumulatorRegisterValue);
+ push(AccumulatorRegisterTag);
+ push(addressTempRegister);
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ subPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ move(AccumulatorRegisterValue, ReturnValueRegisterValue);
+ move(AccumulatorRegisterTag, ReturnValueRegisterTag);
+ addPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(addressTempRegister);
+ pop(AccumulatorRegisterTag);
+ pop(AccumulatorRegisterValue);
+ pop(); // exceptionHandler
+ pop(FramePointerRegister);
+ pop(JSC::ARMRegisters::lr);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), dataTempRegister);
+ call(dataTempRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_ARM32 PlatformAssemblerBase;
+
+#endif
+
+struct PlatformAssemblerCommon : PlatformAssemblerBase
{
- if (barrier)
- *barrier = _function->argLocalRequiresWriteBarrier(al) ? WriteBarrier::Barrier : WriteBarrier::NoBarrier;
+ const Value* constantTable;
+ struct JumpTarget { JSC::MacroAssemblerBase::Jump jump; int offset; };
+ std::vector<JumpTarget> patches;
+ struct ExceptionHanlderTarget { JSC::MacroAssemblerBase::DataLabelPtr label; int offset; };
+ std::vector<ExceptionHanlderTarget> ehTargets;
+ QHash<int, JSC::MacroAssemblerBase::Label> labelsByOffset;
+ QHash<const void *, const char *> functions;
+ std::vector<Jump> catchyJumps;
+ Label functionExit;
+
+ Address exceptionHandlerAddress() const
+ {
+ return Address(FramePointerRegister, -1 * PointerSize);
+ }
- int32_t offset = 0;
- int scope = al->scope;
- loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(EngineBase, current))), baseReg);
+ Address contextAddress() const
+ {
+ return Address(JSStackFrameRegister, offsetof(CallData, context));
+ }
- const qint32 outerOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, outer));
- const qint32 localsOffset = targetStructureOffset(Heap::CallContextData::baseOffset + offsetof(Heap::CallContextData, function))
- + 8 // locals is always 8 bytes away from function, regardless of pointer size.
- + offsetof(ValueArray<0>, values);
+ RegisterID registerForArg(int arg) const
+ {
+ Q_ASSERT(arg >= 0);
+ Q_ASSERT(arg < ArgInRegCount);
+ switch (arg) {
+ case 0: return Arg0Reg;
+ case 1: return Arg1Reg;
+ case 2: return Arg2Reg;
+ case 3: return Arg3Reg;
+ case 4: return Arg4Reg;
+ case 5: return Arg5Reg;
+ case 6: return Arg6Reg;
+ case 7: return Arg7Reg;
+ default:
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ }
+ }
- while (scope) {
- loadPtr(Address(baseReg, outerOffset), baseReg);
- --scope;
+ void callRuntime(const char *functionName, const void *funcPtr)
+ {
+ functions.insert(funcPtr, functionName);
+ callAbsolute(funcPtr);
}
- switch (al->kind) {
- case IR::ArgLocal::Formal:
- case IR::ArgLocal::ScopedFormal: {
- if (barrier && *barrier == WriteBarrier::Barrier) {
- // if we need a barrier, the baseReg has to point to the ExecutionContext
- // callData comes directly after locals, calculate the offset using that
- offset = localsOffset + _function->localsCountForScope(al) * sizeof(Value);
- offset += sizeof(CallData) + (al->index - 1) * sizeof(Value);
- } else {
- const qint32 callDataOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData));
- loadPtr(Address(baseReg, callDataOffset), baseReg);
- offset = sizeof(CallData) + (al->index - 1) * sizeof(Value);
+
+ Address loadFunctionPtr(RegisterID target)
+ {
+ Address addr(CppStackFrameRegister, offsetof(CppStackFrame, v4Function));
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadCompilationUnitPtr(RegisterID target)
+ {
+ Address addr = loadFunctionPtr(target);
+ addr.offset = offsetof(QV4::Function, compilationUnit);
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister)
+ {
+ Address addr = loadCompilationUnitPtr(baseReg);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants);
+ loadPtr(addr, baseReg);
+ addr.offset = constIndex * int(sizeof(QV4::Value));
+ return addr;
+ }
+
+ Address loadStringAddress(int stringId)
+ {
+ Address addr = loadCompilationUnitPtr(ScratchRegister);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings);
+ loadPtr(addr, ScratchRegister);
+ return Address(ScratchRegister, stringId * PointerSize);
+ }
+
+ void passAsArg(RegisterID src, int arg)
+ {
+ move(src, registerForArg(arg));
+ }
+
+ void generateCatchTrampoline(std::function<void()> loadUndefined)
+ {
+ for (Jump j : catchyJumps)
+ j.link(this);
+
+ loadPtr(exceptionHandlerAddress(), ScratchRegister);
+ Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
+ jump(ScratchRegister);
+ exitFunction.link(this);
+ loadUndefined();
+
+ if (functionExit.isSet())
+ jump(functionExit);
+ else
+ generateFunctionExit();
+ }
+
+ void addCatchyJump(Jump j)
+ {
+ Q_ASSERT(j.isSet());
+ catchyJumps.push_back(j);
+ }
+
+ void generateFunctionEntry()
+ {
+ generatePlatformFunctionEntry();
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generateFunctionExit()
+ {
+ if (functionExit.isSet()) {
+ jump(functionExit);
+ return;
}
- } break;
- case IR::ArgLocal::Local:
- case IR::ArgLocal::ScopedLocal: {
- offset = localsOffset + al->index * sizeof(Value);
- } break;
- default:
- Q_UNREACHABLE();
+
+ functionExit = label();
+ generatePlatformFunctionExit();
}
- return Pointer(baseReg, offset);
-}
+};
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string)
+#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler64 : PlatformAssemblerCommon
{
- loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), Assembler::ScratchRegister);
- loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister);
- loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg);
- const int id = _jsGenerator->registerString(string);
- return Pointer(reg, id * RegisterSize);
-}
+ void callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+ {
+ PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
+ if (dest == Assembler::ResultInAccumulator)
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg)
-{
- return loadConstant(convertToValue<TargetPrimitive>(c), baseReg);
-}
+ void loadUndefined(RegisterID dest = AccumulatorRegister)
+ {
+ move(TrustedImm64(0), dest);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg)
-{
- loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg);
- loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg);
- const int index = _jsGenerator->registerConstant(v.rawValue());
- return Address(baseReg, index * sizeof(QV4::Value));
-}
+ void copyConst(int constIndex, Address dest)
+ {
+ //###
+ if (constantTable[constIndex].isUndefined()) {
+ loadUndefined(ScratchRegister);
+ } else {
+ load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
+ }
+ store64(ScratchRegister, dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string)
-{
- const int id = _jsGenerator->registerString(string);
- move(TrustedImm32(id), reg);
-}
+ void copyReg(Address src, Address dst)
+ {
+ loadReg(src, ScratchRegister);
+ store64(ScratchRegister, dst);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination)
-{
- WriteBarrier::Type barrier;
- Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier);
- storeValue(value, addr, barrier);
-}
+ void loadReg(Address addr, RegisterID dest = AccumulatorRegister)
+ {
+ load64(addr, dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave)
-{
- platformEnterStandardStackFrame(this);
+ void loadAccumulator(Address addr)
+ {
+ load64(addr, AccumulatorRegister);
+ }
- move(StackPointerRegister, JITTargetPlatform::FramePointerRegister);
+ void storeAccumulator(Address addr)
+ {
+ store64(AccumulatorRegister, addr);
+ }
- const int frameSize = _stackLayout->calculateStackFrameSize();
- subPtr(TrustedImm32(frameSize), StackPointerRegister);
+ void loadString(int stringId)
+ {
+ loadAccumulator(loadStringAddress(stringId));
+ }
- Address slotAddr(JITTargetPlatform::FramePointerRegister, 0);
- for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) {
- Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint());
- slotAddr.offset -= sizeof(double);
- TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr);
+ void loadValue(ReturnedValue value)
+ {
+ move(TrustedImm64(value), AccumulatorRegister);
}
- for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) {
- Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister());
- slotAddr.offset -= RegisterSize;
- storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr);
+
+ void generateCatchTrampoline()
+ {
+ PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
- platformFinishEnteringStandardStackFrame(this);
-}
+ void toBoolean(std::function<void(RegisterID)> continuation)
+ {
+ urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
+ auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister);
+ continuation(AccumulatorRegister);
+ Jump done = jump();
+
+ // slow path:
+ needsConversion.link(this);
+ push(AccumulatorRegister);
+ move(AccumulatorRegister, registerForArg(0));
+ callHelper(Value::toBooleanImpl);
+ and32(TrustedImm32(1), ReturnValueRegister, ScratchRegister);
+ pop(AccumulatorRegister);
+ continuation(ScratchRegister);
+
+ done.link(this);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave)
-{
- Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double));
+ void toNumber()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callHelper(toNumberHelper);
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
- // restore the callee saved registers
- for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) {
- Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister());
- loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>());
- slotAddr.offset += RegisterSize;
+ void toInt32()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
+ Assembler::ResultInAccumulator);
}
- for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) {
- Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint());
- TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>());
- slotAddr.offset += sizeof(double);
+
+ void regToInt32(Address srcReg, RegisterID targetReg)
+ {
+ pushAligned(AccumulatorRegister);
+ load64(srcReg, registerForArg(0));
+ callHelper(toInt32Helper);
+ move(ReturnValueRegister, targetReg);
+ popAligned(AccumulatorRegister);
}
- Q_ASSERT(slotAddr.offset == 0);
+ void isNullOrUndefined()
+ {
+ move(AccumulatorRegister, ScratchRegister);
+ compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister);
+ Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister);
- const int frameSize = _stackLayout->calculateStackFrameSize();
- platformLeaveStandardStackFrame(this, frameSize);
-}
+ // not undefined
+ rshift64(TrustedImm32(32), ScratchRegister);
+ compare32(Equal, ScratchRegister, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
+ AccumulatorRegister);
+ isUndef.link(this);
+ }
+ void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+ {
+ Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
+ load64(lhsAddr, ScratchRegister);
+ Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
+ Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
+ patches.push_back({ equal, offset });
+ isUndef.link(this);
+ }
+ void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+ {
+ Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
+ load64(lhsAddr, ScratchRegister);
+ Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
+ patches.push_back({ isUndef, offset });
+ Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
+ patches.push_back({ notEqual, offset });
+ }
-// Try to load the source expression into the destination FP register. This assumes that two
-// general purpose (integer) registers are available: the ScratchRegister and the
-// ReturnValueRegister. It returns a Jump if no conversion can be performed.
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest)
-{
- switch (src->type) {
- case IR::DoubleType:
- moveDouble(toDoubleRegister(src, dest), dest);
- return Assembler::Jump();
- case IR::SInt32Type:
- convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister),
- dest);
- return Assembler::Jump();
- case IR::UInt32Type:
- convertUInt32ToDouble(toUInt32Register(src, Assembler::ScratchRegister),
- dest, Assembler::ReturnValueRegister);
- return Assembler::Jump();
- case IR::NullType:
- case IR::UndefinedType:
- case IR::BoolType:
- // TODO?
- case IR::StringType:
- return jump();
- default:
- break;
+ void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
+ {
+ if (sourceReg == NoRegister)
+ or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister);
+ else
+ or64(TrustedImm64(int64_t(tag) << 32), sourceReg, AccumulatorRegister);
}
- Q_ASSERT(src->asTemp() || src->asArgLocal());
+ void encodeDoubleIntoAccumulator(FPRegisterID src)
+ {
+ moveDoubleTo64(src, AccumulatorRegister);
+ move(TrustedImm64(Value::NaNEncodeMask), ScratchRegister);
+ xor64(ScratchRegister, AccumulatorRegister);
+ }
- // It's not a number type, so it cannot be in a register.
- Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType);
+ void pushValue(ReturnedValue v)
+ {
+ loadValue(v);
+ push(AccumulatorRegister);
+ }
- Assembler::Pointer tagAddr = loadAddressForReading(Assembler::ScratchRegister, src);
- tagAddr.offset += 4;
- load32(tagAddr, Assembler::ScratchRegister);
+ void pushValueAligned(ReturnedValue v)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ pushValue(v);
+ }
- // check if it's an int32:
- Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister,
- Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer)));
- convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest);
- Assembler::Jump intDone = jump();
+ void popValueAligned()
+ {
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
- // not an int, check if it's a double:
- isNoInt.link(this);
- Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister);
- toDoubleRegister(src, dest);
- intDone.link(this);
+ Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
+ {
+ urshift64(AccumulatorRegister, TrustedImm32(32), ScratchRegister);
+ Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
+ load64(lhsAddr, ScratchRegister);
+ urshift64(ScratchRegister, TrustedImm32(32), ScratchRegister2);
+ Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister2);
+
+ // both integer
+ Jump failure = fastPath();
+ Jump done = jump();
+
+ // all other cases
+ if (failure.isSet())
+ failure.link(this);
+ accNotInt.link(this);
+ lhsNotInt.link(this);
+
+ return done;
+ }
+};
- return isNoDbl;
-}
+typedef PlatformAssembler64 PlatformAssembler;
+#endif
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op,
- IR::Expr *left, IR::Expr *right)
+#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler32 : PlatformAssemblerCommon
{
- DoubleCondition cond;
- switch (op) {
- case IR::OpGt: cond = Assembler::DoubleGreaterThan; break;
- case IR::OpLt: cond = Assembler::DoubleLessThan; break;
- case IR::OpGe: cond = Assembler::DoubleGreaterThanOrEqual; break;
- case IR::OpLe: cond = Assembler::DoubleLessThanOrEqual; break;
- case IR::OpEqual:
- case IR::OpStrictEqual: cond = Assembler::DoubleEqual; break;
- case IR::OpNotEqual:
- case IR::OpStrictNotEqual: cond = Assembler::DoubleNotEqualOrUnordered; break; // No, the inversion of DoubleEqual is NOT DoubleNotEqual.
- default:
- Q_UNREACHABLE();
+ void callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+ {
+ PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
+ if (dest == Assembler::ResultInAccumulator) {
+ move(ReturnValueRegisterValue, AccumulatorRegisterValue);
+ move(ReturnValueRegisterTag, AccumulatorRegisterTag);
+ }
}
- if (invertCondition)
- cond = TargetConfiguration::MacroAssembler::invert(cond);
- return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1));
-}
+ void loadUndefined()
+ {
+ move(TrustedImm32(0), AccumulatorRegisterValue);
+ move(TrustedImm32(0), AccumulatorRegisterTag);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right)
-{
- Assembler::RelationalCondition cond;
- switch (op) {
- case IR::OpGt: cond = Assembler::GreaterThan; break;
- case IR::OpLt: cond = Assembler::LessThan; break;
- case IR::OpGe: cond = Assembler::GreaterThanOrEqual; break;
- case IR::OpLe: cond = Assembler::LessThanOrEqual; break;
- case IR::OpEqual:
- case IR::OpStrictEqual: cond = Assembler::Equal; break;
- case IR::OpNotEqual:
- case IR::OpStrictNotEqual: cond = Assembler::NotEqual; break;
- default:
- Q_UNREACHABLE();
+ void copyConst(int constIndex, Address destRegAddr)
+ {
+ //###
+ if (constantTable[constIndex].isUndefined()) {
+ move(TrustedImm32(0), ScratchRegister);
+ store32(ScratchRegister, destRegAddr);
+ destRegAddr.offset += 4;
+ store32(ScratchRegister, destRegAddr);
+ } else {
+ Address src = loadConstAddress(constIndex);
+ loadDouble(src, FPScratchRegister);
+ storeDouble(FPScratchRegister, destRegAddr);
+ }
}
- if (invertCondition)
- cond = TargetConfiguration::MacroAssembler::invert(cond);
-
- return TargetConfiguration::MacroAssembler::branch32(cond,
- toInt32Register(left, Assembler::ScratchRegister),
- toInt32Register(right, Assembler::ReturnValueRegister));
-}
-
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave)
-{
- _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave));
-}
-
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave)
-{
- if (!s) {
- // this only happens if the method doesn't have a return statement and can
- // only exit through an exception
- } else if (IR::Temp *t = s->expr->asTemp()) {
- RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t);
- } else if (IR::Const *c = s->expr->asConst()) {
- auto retVal = convertToValue<TargetPrimitive>(c);
- RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal);
- } else {
- Q_UNREACHABLE();
- Q_UNUSED(s);
+
+ void copyReg(Address src, Address dest)
+ {
+ loadDouble(src, FPScratchRegister);
+ storeDouble(FPScratchRegister, dest);
+ }
+
+ void loadReg(Address addr)
+ {
+ load32(addr, AccumulatorRegisterValue);
+ addr.offset += 4;
+ load32(addr, AccumulatorRegisterTag);
+ }
+
+ void loadAccumulator(Address src)
+ {
+ load32(src, AccumulatorRegisterValue);
+ src.offset += 4;
+ load32(src, AccumulatorRegisterTag);
+ }
+
+ void storeAccumulator(Address addr)
+ {
+ store32(AccumulatorRegisterValue, addr);
+ addr.offset += 4;
+ store32(AccumulatorRegisterTag, addr);
+ }
+
+ void loadString(int stringId)
+ {
+ load32(loadStringAddress(stringId), AccumulatorRegisterValue);
+ move(TrustedImm32(0), AccumulatorRegisterTag);
+ }
+
+ void loadValue(ReturnedValue value)
+ {
+ move(TrustedImm32(Value::fromReturnedValue(value).value()), AccumulatorRegisterValue);
+ move(TrustedImm32(Value::fromReturnedValue(value).tag()), AccumulatorRegisterTag);
+ }
+
+ void generateCatchTrampoline()
+ {
+ PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
+ }
+
+ void toNumber()
+ {
+ if (ArgInRegCount < 2) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callRuntime("toNumberHelper", reinterpret_cast<void *>(&toNumberHelper),
+ Assembler::ResultInAccumulator);
+ move(ReturnValueRegisterValue, AccumulatorRegisterValue);
+ move(ReturnValueRegisterTag, AccumulatorRegisterTag);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
+
+ void toInt32()
+ {
+ if (ArgInRegCount < 2) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
+ Assembler::ResultInAccumulator);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
+
+ void regToInt32(Address srcReg, RegisterID targetReg)
+ {
+ bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
+ || AccumulatorRegisterTag == ReturnValueRegisterTag;
+ if (accumulatorNeedsSaving) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ }
+ if (ArgInRegCount < 2) {
+ srcReg.offset += 4;
+ load32(srcReg, targetReg);
+ push(targetReg);
+ srcReg.offset -= 4;
+ load32(srcReg, targetReg);
+ push(targetReg);
+ } else {
+ load32(srcReg, registerForArg(0));
+ srcReg.offset += 4;
+ load32(srcReg, registerForArg(1));
+ }
+ callHelper(toInt32Helper);
+ move(ReturnValueRegisterValue, targetReg);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ if (accumulatorNeedsSaving) {
+ pop(AccumulatorRegisterValue);
+ pop(AccumulatorRegisterTag);
+ }
+ }
+
+ void isNullOrUndefined()
+ {
+ Jump notUndefOrPtr = branch32(NotEqual, TrustedImm32(0), AccumulatorRegisterTag);
+ compare32(Equal, AccumulatorRegisterValue, TrustedImm32(0), AccumulatorRegisterValue);
+ auto done = jump();
+
+ // not undefined or managed
+ notUndefOrPtr.link(this);
+ compare32(Equal, AccumulatorRegisterTag, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
+ AccumulatorRegisterValue);
+
+ done.link(this);
+ }
+
+ void pushValue(ReturnedValue v)
+ {
+ push(TrustedImm32(v >> 32));
+ push(TrustedImm32(v));
+ }
+
+ void toBoolean(std::function<void(RegisterID)> continuation)
+ {
+ urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
+ ScratchRegister);
+ auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister);
+ continuation(AccumulatorRegisterValue);
+ Jump done = jump();
+
+ // slow path:
+ needsConversion.link(this);
+
+ bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
+ || AccumulatorRegisterTag == ReturnValueRegisterTag;
+ if (accumulatorNeedsSaving) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ }
+
+ if (ArgInRegCount < 2) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callHelper(Value::toBooleanImpl);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+
+ and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister);
+ if (accumulatorNeedsSaving) {
+ pop(AccumulatorRegisterValue);
+ pop(AccumulatorRegisterTag);
+ }
+ continuation(ScratchRegister);
+
+ done.link(this);
+ }
+
+ void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+ {
+ Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
+ load32(lhsAddr, ScratchRegister);
+ Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
+ Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
+ patches.push_back({ notEqUndefVal, offset });
+ lhsAddr.offset += 4;
+ load32(lhsAddr, ScratchRegister);
+ Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
+ patches.push_back({ notEqUndefTag, offset });
+ notEqInt.link(this);
+ }
+
+ void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+ {
+ Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
+ load32(lhsAddr, ScratchRegister);
+ Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
+ patches.push_back({ notEqual, offset });
+ Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
+ lhsAddr.offset += 4;
+ load32(lhsAddr, ScratchRegister);
+ Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
+ patches.push_back({ equalUndef, offset });
+ notUndefValue.link(this);
+ }
+
+ void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
+ {
+ if (sourceReg != NoRegister)
+ move(sourceReg, AccumulatorRegisterValue);
+ move(TrustedImm32(int(tag)), AccumulatorRegisterTag);
+ }
+
+ void encodeDoubleIntoAccumulator(FPRegisterID src)
+ {
+ moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag);
+ xor32(TrustedImm32(Value::NaNEncodeMask >> 32), AccumulatorRegisterTag);
+ }
+
+ void pushValueAligned(ReturnedValue v)
+ {
+ pushValue(v);
}
- Label leaveStackFrame = label();
+ void popValueAligned()
+ {
+ popValue();
+ }
+
+ Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
+ {
+ Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
+ Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset();
+ load32(lhsAddrTag, ScratchRegister);
+ Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
+
+ // both integer
+ Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset();
+ load32(lhsAddrValue, ScratchRegister);
+ Jump failure = fastPath();
+ Jump done = jump();
+
+ // all other cases
+ if (failure.isSet())
+ failure.link(this);
+ accNotInt.link(this);
+ lhsNotInt.link(this);
+
+ return done;
+ }
+};
+
+typedef PlatformAssembler32 PlatformAssembler;
+#endif
+
+typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr;
+typedef PlatformAssembler::TrustedImm32 TrustedImm32;
+typedef PlatformAssembler::TrustedImm64 TrustedImm64;
+typedef PlatformAssembler::Address Address;
+typedef PlatformAssembler::RegisterID RegisterID;
+typedef PlatformAssembler::FPRegisterID FPRegisterID;
- const int locals = stackLayout().calculateJSStackFrameSize();
- subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister);
- storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))));
+#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
- leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave);
- ret();
+static Address regAddr(int reg)
+{
+ return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
+}
+
+Assembler::Assembler(const Value *constantTable)
+ : d(new PlatformAssembler)
+{
+ pasm()->constantTable = constantTable;
+}
- exceptionReturnLabel = label();
- auto retVal = TargetPrimitive::undefinedValue();
- RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal);
- jump(leaveStackFrame);
+Assembler::~Assembler()
+{
+ delete pasm();
+}
+
+void Assembler::generatePrologue()
+{
+ pasm()->generateFunctionEntry();
+}
+
+void Assembler::generateEpilogue()
+{
+ pasm()->generateCatchTrampoline();
}
namespace {
@@ -560,7 +1155,7 @@ public:
~QIODevicePrintStream()
{}
- void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0)
+ void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0)
{
const int written = qvsnprintf(buf.data(), buf.size(), format, argList);
if (written > 0)
@@ -568,7 +1163,7 @@ public:
memset(buf.data(), 0, qMin(written, buf.size()));
}
- void flush() override
+ void flush()
{}
private:
@@ -577,150 +1172,792 @@ private:
};
} // anonymous namespace
-static void printDisassembledOutputWithCalls(QByteArray processedOutput, const QHash<void*, const char*>& functions)
+static void printDisassembledOutputWithCalls(QByteArray processedOutput,
+ const QHash<const void*, const char*>& functions)
{
- for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ for (QHash<const void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
it != end; ++it) {
const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16);
- int idx = processedOutput.indexOf(ptrString);
- if (idx < 0)
- continue;
- idx = processedOutput.lastIndexOf('\n', idx);
- if (idx < 0)
- continue;
- processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; call ") + it.value());
+ int idx = 0;
+ while (idx >= 0) {
+ idx = processedOutput.indexOf(ptrString, idx);
+ if (idx < 0)
+ break;
+ idx = processedOutput.indexOf('\n', idx);
+ if (idx < 0)
+ break;
+ processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value());
+ }
}
qDebug("%s", processedOutput.constData());
}
-#if defined(Q_OS_LINUX)
-static FILE *pmap;
+void Assembler::link(Function *function)
+{
+ for (const auto &jumpTarget : pasm()->patches)
+ jumpTarget.jump.linkTo(pasm()->labelsByOffset[jumpTarget.offset], pasm());
+
+ JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
+ JSC::LinkBuffer<PlatformAssembler::MacroAssembler> linkBuffer(dummy, pasm(), 0);
+
+ for (const auto &ehTarget : pasm()->ehTargets) {
+ auto targetLabel = pasm()->labelsByOffset.value(ehTarget.offset);
+ linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel));
+ }
+
+ JSC::MacroAssemblerCodeRef codeRef;
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
+ if (showCode) {
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ WTF::setDataFile(new QIODevicePrintStream(&buf));
+
+ QByteArray name = function->name()->toQString().toUtf8();
+ if (name.isEmpty()) {
+ name = QByteArray::number(quintptr(function), 16);
+ name.prepend("QV4::Function(0x");
+ name.append(')');
+ }
+ codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+
+ WTF::setDataFile(stderr);
+ printDisassembledOutputWithCalls(buf.data(), pasm()->functions);
+ } else {
+ codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ }
+
+ function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
+ function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
+}
+
+void Assembler::addLabel(int offset)
+{
+ pasm()->labelsByOffset[offset] = pasm()->label();
+}
-static void qt_closePmap()
+void Assembler::loadConst(int constIndex)
{
- if (pmap) {
- fclose(pmap);
- pmap = 0;
+ //###
+ if (pasm()->constantTable[constIndex].isUndefined()) {
+ pasm()->loadUndefined();
+ } else {
+ pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
}
}
-#endif
+void Assembler::copyConst(int constIndex, int destReg)
+{
+ pasm()->copyConst(constIndex, regAddr(destReg));
+}
-template <typename TargetConfiguration>
-JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize)
+void Assembler::loadReg(int reg)
{
- Label endOfCode = label();
+ pasm()->loadReg(regAddr(reg));
+}
- {
- for (size_t i = 0, ei = _patches.size(); i != ei; ++i) {
- Label target = _addrs.at(i);
- Q_ASSERT(target.isSet());
- for (Jump jump : qAsConst(_patches.at(i)))
- jump.linkTo(target, this);
- }
- }
+void Assembler::storeReg(int reg)
+{
+ pasm()->storeAccumulator(regAddr(reg));
+}
- JSC::JSGlobalData dummy(_executableAllocator);
- JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0);
+void Assembler::loadString(int stringId)
+{
+ pasm()->loadString(stringId);
+}
- for (const DataLabelPatch &p : qAsConst(_dataLabelPatches))
- linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
+void Assembler::loadValue(ReturnedValue value)
+{
+ pasm()->loadValue(value);
+}
- // link exception handlers
- for (Jump jump : qAsConst(exceptionPropagationJumps))
- linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel));
+void Assembler::toNumber()
+{
+ pasm()->toNumber();
+}
- {
- for (size_t i = 0, ei = _labelPatches.size(); i != ei; ++i) {
- Label target = _addrs.at(i);
- Q_ASSERT(target.isSet());
- for (DataLabelPtr label : _labelPatches.at(i))
- linkBuffer.patch(label, linkBuffer.locationOf(target));
- }
- }
+void Assembler::uminus()
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, ResultInAccumulator);
+ checkException();
+}
- *codeSize = linkBuffer.offsetOf(endOfCode);
+void Assembler::ucompl()
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
- QByteArray name;
+static ReturnedValue incHelper(const Value &v)
+{
+ return Encode(v.toNumber() + 1.);
+}
- JSC::MacroAssemblerCodeRef codeRef;
+void Assembler::inc()
+{
+// auto done = pasm()->incFastPath();
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
- if (showCode) {
- QHash<void*, const char*> functions;
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(incHelper, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+static ReturnedValue decHelper(const Value &v)
+{
+ return Encode(v.toNumber() - 1.);
+}
+
+void Assembler::dec()
+{
+// auto done = pasm()->decFastPath();
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(decHelper, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::unot()
+{
+ pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){
+ pasm()->compare32(PlatformAssembler::Equal, resultReg,
+ TrustedImm32(0), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+ });
+}
+
+void Assembler::add(int lhs)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(3);
+ passAccumulatorAsArg(2);
+ passRegAsArg(lhs, 1);
+ passEngineAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_add, ResultInAccumulator);
+ checkException();
+
+ // done.
+ done.link(pasm());
+}
+
+void Assembler::bitAnd(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->and32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitOr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->or32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitXor(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->xor32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::ushr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->urshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
+ PlatformAssembler::AccumulatorRegisterValue,
+ TrustedImm32(0));
+ pasm()->setAccumulatorTag(IntegerTag);
+ auto done = pasm()->jump();
+
+ doubleEncode.link(pasm());
+ pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::FPScratchRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
+ done.link(pasm());
+}
+
+void Assembler::shr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->rshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::shl(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->lshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitAndConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitOrConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitXorConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::ushrConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->urshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
+ PlatformAssembler::AccumulatorRegisterValue,
+ TrustedImm32(0));
+ pasm()->setAccumulatorTag(IntegerTag);
+ auto done = pasm()->jump();
+
+ doubleEncode.link(pasm());
+ pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::FPScratchRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
+ done.link(pasm());
+}
+
+void Assembler::shrConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->rshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::shlConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->lshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::mul(int lhs)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mul, ResultInAccumulator);
+ checkException();
+
+ // done.
+ done.link(pasm());
+}
+
+void Assembler::div(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_div, ResultInAccumulator);
+ checkException();
+}
+
+void Assembler::mod(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mod, ResultInAccumulator);
+ checkException();
+}
+
+void Assembler::sub(int lhs)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_sub, ResultInAccumulator);
+ checkException();
+
+ // done.
+ done.link(pasm());
+}
+
+void Assembler::cmpeqNull()
+{
+ pasm()->isNullOrUndefined();
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpneNull()
+{
+ pasm()->isNullOrUndefined();
+ pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpeqInt(int lhs)
+{
+ saveAccumulatorInFrame();
+ pasm()->pushValueAligned(Encode(lhs));
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->push(PlatformAssembler::StackPointerRegister);
+ else
+ pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
+ passAccumulatorAsArg_internal(0, true);
+ pasm()->callRuntime("Runtime::method_equal", (void*)Runtime::method_equal, ResultInAccumulator);
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
+ pasm()->popValueAligned();
+}
+
+void Assembler::cmpneInt(int lhs)
+{
+ saveAccumulatorInFrame();
+ pasm()->pushValueAligned(Encode(lhs));
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->push(PlatformAssembler::StackPointerRegister);
+ else
+ pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
+ passAccumulatorAsArg_internal(0, true);
+ pasm()->callRuntime("Runtime::method_notEqual", (void*)Runtime::method_notEqual, ResultInAccumulator);
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
+ pasm()->popValueAligned();
+}
+
+void Assembler::cmp(int /*cond*/, CmpFunc function, const char *functionName, int lhs)
+{
+// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister, lhs * int(sizeof(QV4::Value)));
+// auto done = pasm()->cmpFastPath(static_cast<PlatformAssembler::RelationalCondition>(cond), lhsAddr);
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+
+ callRuntime(functionName, reinterpret_cast<void*>(function), ResultInAccumulator);
+ checkException();
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::cmpeq(int lhs)
+{
+ cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual,
+ "Runtime::method_compareEqual", lhs);
+}
+
+void Assembler::cmpne(int lhs)
+{
+ cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual,
+ "Runtime::method_compareNotEqual", lhs);
+}
+
+void Assembler::cmpgt(int lhs)
+{
+ cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan,
+ "Runtime::method_compareGreaterThan", lhs);
+}
+
+void Assembler::cmpge(int lhs)
+{
+ cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual,
+ "Runtime::method_compareGreaterEqual", lhs);
+}
+
+void Assembler::cmplt(int lhs)
+{
+ cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan,
+ "Runtime::method_compareLessThan", lhs);
+}
+
+void Assembler::cmple(int lhs)
+{
+ cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual,
+ "Runtime::method_compareLessEqual", lhs);
+}
+
+void Assembler::cmpStrictEqual(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpStrictNotEqual(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator);
+ pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::jump(int offset)
+{
+ pasm()->patches.push_back({ pasm()->jump(), offset });
+}
+
+void Assembler::jumpTrue(int offset)
+{
+ pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
+ auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
+ pasm()->patches.push_back({ jump, offset });
+ });
+}
+
+void Assembler::jumpFalse(int offset)
+{
+ pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
+ pasm()->patches.push_back({ jump, offset });
+ });
+}
+
+void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
+}
+
+void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
+}
+
+void Assembler::prepareCallWithArgCount(int argc)
+{
#ifndef QT_NO_DEBUG
- for (CallInfo call : qAsConst(_callInfos))
- functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName;
+ Q_ASSERT(remainingArgcForCall == NoCall);
+ remainingArgcForCall = argc;
#endif
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- WTF::setDataFile(new QIODevicePrintStream(&buf));
+ if (argc > PlatformAssembler::ArgInRegCount) {
+ argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - PlatformAssembler::ArgInRegCount) * PlatformAssembler::PointerSize));
+ pasm()->subPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
+ }
+}
- name = _function->name->toUtf8();
- if (name.isEmpty())
- name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')';
- codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+void Assembler::storeInstructionPointer(int instructionOffset)
+{
+ PlatformAssembler::Address addr(PlatformAssembler::CppStackFrameRegister,
+ offsetof(QV4::CppStackFrame, instructionPointer));
+ pasm()->store32(TrustedImm32(instructionOffset), addr);
+}
- WTF::setDataFile(stderr);
- printDisassembledOutputWithCalls(buf.data(), functions);
+Address argStackAddress(int arg)
+{
+ int offset = arg - PlatformAssembler::ArgInRegCount;
+ Q_ASSERT(offset >= 0);
+ return Address(PlatformAssembler::StackPointerRegister, offset * PlatformAssembler::PointerSize);
+}
+
+void Assembler::passAccumulatorAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ passAccumulatorAsArg_internal(arg, false);
+}
+
+void Assembler::passAccumulatorAsArg_internal(int arg, bool push)
+{
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
+ PlatformAssembler::JSStackFrameRegister,
+ pasm()->registerForArg(arg));
} else {
- codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
+ PlatformAssembler::JSStackFrameRegister,
+ PlatformAssembler::ScratchRegister);
+ if (push)
+ pasm()->push(PlatformAssembler::ScratchRegister);
+ else
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
}
+}
-#if defined(Q_OS_LINUX)
- // This implements writing of JIT'd addresses so that perf can find the
- // symbol names.
- //
- // Perf expects the mapping to be in a certain place and have certain
- // content, for more information, see:
- // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
- static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
- static bool profileInitialized = false;
- if (doProfile && !profileInitialized) {
- profileInitialized = true;
+void Assembler::passFunctionAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- char pname[PATH_MAX];
- snprintf(pname, PATH_MAX - 1, "/tmp/perf-%lu.map",
- (unsigned long)QCoreApplication::applicationPid());
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->loadFunctionPtr(pasm()->registerForArg(arg));
+ } else {
+ pasm()->loadFunctionPtr(PlatformAssembler::ScratchRegister);
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
+ }
+}
- pmap = fopen(pname, "w");
- if (!pmap)
- qWarning("QV4: Can't write %s, call stacks will not contain JavaScript function names", pname);
+void Assembler::passEngineAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- // make sure we clean up nicely
- std::atexit(qt_closePmap);
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::EngineRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->storePtr(PlatformAssembler::EngineRegister, argStackAddress(arg));
}
+}
- if (pmap) {
- // this may have been pre-populated, if QV4_SHOW_ASM was on
- if (name.isEmpty()) {
- name = _function->name->toUtf8();
- if (name.isEmpty())
- name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')';
- }
+void Assembler::passRegAsArg(int reg, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- fprintf(pmap, "%llx %x %.*s\n",
- (long long unsigned int)codeRef.code().executableAddress(),
- *codeSize,
- name.length(),
- name.constData());
- fflush(pmap);
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
+ PlatformAssembler::JSStackFrameRegister,
+ pasm()->registerForArg(arg));
+ } else {
+ pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
+ PlatformAssembler::JSStackFrameRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
}
+}
+
+void JIT::Assembler::passCppFrameAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
#endif
- return codeRef;
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::CppStackFrameRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->store32(PlatformAssembler::CppStackFrameRegister, argStackAddress(arg));
+ }
}
-template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>;
-#if defined(V4_BOOTSTRAP)
-#if !CPU(ARM_THUMB2)
-template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
-#endif
-#if !CPU(ARM64)
-template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
-#endif
+void Assembler::passInt32AsArg(int value, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
#endif
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(TrustedImm32(value), pasm()->registerForArg(arg));
+ } else {
+ pasm()->store32(TrustedImm32(value), argStackAddress(arg));
+ }
+}
+
+void Assembler::callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(remainingArgcForCall == 0);
+ remainingArgcForCall = NoCall;
#endif
+ pasm()->callRuntime(functionName, funcPtr, dest);
+ if (argcOnStackForCall > 0) {
+ pasm()->addPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
+ argcOnStackForCall = 0;
+ }
+}
+
+void Assembler::saveAccumulatorInFrame()
+{
+ pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
+ offsetof(CallData, accumulator)));
+}
+
+void Assembler::checkException()
+{
+ pasm()->addCatchyJump(
+ pasm()->branch32(
+ PlatformAssembler::NotEqual,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0)));
+}
+
+void Assembler::gotoCatchException()
+{
+ pasm()->addCatchyJump(pasm()->jump());
+}
+
+void Assembler::getException()
+{
+ Q_STATIC_ASSERT_FOR_SANE_COMPILERS(sizeof(QV4::EngineBase::hasException) == 1);
+
+ Address hasExceptionAddr(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException));
+ PlatformAssembler::Jump nope = pasm()->branch8(PlatformAssembler::Equal,
+ hasExceptionAddr,
+ TrustedImm32(0));
+ pasm()->loadPtr(Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, exceptionValue)),
+ PlatformAssembler::ScratchRegister);
+ pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
+ pasm()->store8(TrustedImm32(0), hasExceptionAddr);
+ auto done = pasm()->jump();
+ nope.link(pasm());
+ pasm()->loadValue(Primitive::emptyValue().asReturnedValue());
+
+ done.link(pasm());
+}
+
+void Assembler::setException()
+{
+ Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
+ pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
+ pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
+ addr.offset = offsetof(EngineBase, hasException);
+ Q_STATIC_ASSERT_FOR_SANE_COMPILERS(sizeof(QV4::EngineBase::hasException) == 1);
+ pasm()->store8(TrustedImm32(1), addr);
+}
+
+void Assembler::setExceptionHandler(int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
+ pasm()->ehTargets.push_back({ l, offset });
+}
+
+
+void Assembler::clearExceptionHandler()
+{
+ pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
+}
+
+void Assembler::pushCatchContext(int name, int reg)
+{
+ pasm()->copyReg(pasm()->contextAddress(), regAddr(reg));
+ prepareCallWithArgCount(2);
+ passInt32AsArg(name, 1);
+ passRegAsArg(CallData::Context, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator);
+ pasm()->storeAccumulator(pasm()->contextAddress());
+}
+
+void Assembler::popContext(int reg)
+{
+ pasm()->copyReg(regAddr(reg), pasm()->contextAddress());
+}
+
+void Assembler::ret()
+{
+ pasm()->generateFunctionExit();
+}
+
+} // JIT namespace
+} // QV4 namepsace
+
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 2ae4818814..eafe27c8bb 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -36,6 +36,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
#ifndef QV4ASSEMBLER_P_H
#define QV4ASSEMBLER_P_H
@@ -50,1802 +51,138 @@
// We mean it.
//
-#include "private/qv4global_p.h"
-#include "private/qv4jsir_p.h"
-#include "private/qv4isel_p.h"
-#include "private/qv4isel_util_p.h"
-#include "private/qv4value_p.h"
-#include "private/qv4context_p.h"
-#include "private/qv4engine_p.h"
-#include "private/qv4writebarrier_p.h"
-#include "qv4targetplatform_p.h"
-
-#include <config.h>
-#include <wtf/Vector.h>
-
-#include <climits>
-
-#if ENABLE(ASSEMBLER)
-
-#include <assembler/MacroAssembler.h>
-#include <assembler/MacroAssemblerCodeRef.h>
+#include <private/qv4global_p.h>
+#include <private/qv4function_p.h>
+#include <QHash>
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
-struct CompilationUnit : public QV4::CompiledData::CompilationUnit
-{
- virtual ~CompilationUnit();
-
-#if !defined(V4_BOOTSTRAP)
- void linkBackendToEngine(QV4::ExecutionEngine *engine) override;
- bool memoryMapCode(QString *errorString) override;
-#endif
- void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) override;
- bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) override;
-
- // Coderef + execution engine
-
- QVector<JSC::MacroAssemblerCodeRef> codeRefs;
-};
-
-template <typename PlatformAssembler, TargetOperatingSystemSpecialization Specialization>
-struct AssemblerTargetConfiguration
-{
- typedef JSC::MacroAssembler<PlatformAssembler> MacroAssembler;
- typedef TargetPlatform<PlatformAssembler, Specialization> Platform;
- // More things coming here in the future, such as Target OS
-};
-
-#if CPU(ARM_THUMB2)
-typedef JSC::MacroAssemblerARMv7 DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#elif CPU(ARM64)
-typedef JSC::MacroAssemblerARM64 DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#elif CPU(ARM_TRADITIONAL)
-typedef JSC::MacroAssemblerARM DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#elif CPU(MIPS)
-typedef JSC::MacroAssemblerMIPS DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#elif CPU(X86)
-typedef JSC::MacroAssemblerX86 DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#elif CPU(X86_64)
-typedef JSC::MacroAssemblerX86_64 DefaultPlatformMacroAssembler;
-
-#if OS(WINDOWS)
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, WindowsSpecialization> DefaultAssemblerTargetConfiguration;
-#else
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#endif
-
-#elif CPU(SH4)
-typedef JSC::MacroAssemblerSH4 DefaultPlatformMacroAssembler;
-typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration;
-#endif
-
-#define isel_stringIfyx(s) #s
-#define isel_stringIfy(s) isel_stringIfyx(s)
-
-#define generateRuntimeCall(as, t, function, ...) \
- as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(QV4::Runtime::function), __VA_ARGS__)
-
-
-template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize>
-struct RegisterSizeDependentAssembler
-{
-};
-
-template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform>
-struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 4>
-{
- using RegisterID = typename JITAssembler::RegisterID;
- using FPRegisterID = typename JITAssembler::FPRegisterID;
- using RelationalCondition = typename JITAssembler::RelationalCondition;
- using ResultCondition = typename JITAssembler::ResultCondition;
- using Address = typename JITAssembler::Address;
- using Pointer = typename JITAssembler::Pointer;
- using TrustedImm32 = typename JITAssembler::TrustedImm32;
- using TrustedImm64 = typename JITAssembler::TrustedImm64;
- using Jump = typename JITAssembler::Jump;
- using Label = typename JITAssembler::Label;
- using ValueTypeInternal = Value::ValueTypeInternal_32;
- using TargetPrimitive = TargetPrimitive32;
-
- static void emitSetGrayBit(JITAssembler *as, RegisterID base)
- {
- bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister);
-
- as->push(TargetPlatform::EngineRegister); // free up one register for work
-
- RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister;
- as->move(base, grayBitmap);
- Q_ASSERT(base != grayBitmap);
- as->urshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap);
- as->lshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap);
- Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0);
-
- RegisterID index = base;
- as->move(base, index);
- as->sub32(grayBitmap, index);
- as->urshift32(TrustedImm32(Chunk::SlotSizeShift), index);
- RegisterID grayIndex = TargetPlatform::EngineRegister;
- as->move(index, grayIndex);
- as->urshift32(TrustedImm32(Chunk::BitShift), grayIndex);
- as->lshift32(TrustedImm32(2), grayIndex); // 4 bytes per quintptr
- as->add32(grayIndex, grayBitmap);
- as->and32(TrustedImm32(Chunk::Bits - 1), index);
-
- RegisterID bit = TargetPlatform::EngineRegister;
- as->move(TrustedImm32(1), bit);
- as->lshift32(index, bit);
-
- as->load32(Pointer(grayBitmap, 0), index);
- as->or32(bit, index);
- as->store32(index, Pointer(grayBitmap, 0));
-
- as->pop(TargetPlatform::EngineRegister);
- }
-
-#if WRITEBARRIER(none)
- static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {}
-#endif
-
- static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest)
- {
- as->MacroAssembler::loadDouble(addr, dest);
- }
-
- static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier)
- {
- as->MacroAssembler::storeDouble(source, addr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, addr);
- }
-
- static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target)
- {
- WriteBarrier::Type barrier;
- Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier);
- as->storeDouble(source, ptr, barrier);
- }
-
- static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier)
- {
- as->store32(TrustedImm32(value.value()), destination);
- destination.offset += 4;
- as->store32(TrustedImm32(value.tag()), destination);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, destination);
- }
-
- template <typename Source, typename Destination>
- static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier)
- {
- as->loadDouble(source, TargetPlatform::FPGpr0);
- // We need to pass NoBarrier to storeDouble and call emitWriteBarrier ourselves, as the
- // code in storeDouble assumes the type we're storing is actually a double, something
- // that isn't always the case here.
- as->storeDouble(TargetPlatform::FPGpr0, destination, WriteBarrier::NoBarrier);
- if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, destination);
- }
-
- static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target)
- {
- as->MacroAssembler::loadDouble(as->loadConstant(c, TargetPlatform::ScratchRegister), target);
- }
-
- static void storeReturnValue(JITAssembler *as, FPRegisterID dest)
- {
- as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0);
- }
-
- static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier)
- {
- Address destination = dest;
- as->store32(TargetPlatform::LowReturnValueRegister, destination);
- destination.offset += 4;
- as->store32(TargetPlatform::HighReturnValueRegister, destination);
- if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, dest);
- }
-
- static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t)
- {
- const auto lowReg = TargetPlatform::LowReturnValueRegister;
- const auto highReg = TargetPlatform::HighReturnValueRegister;
-
- if (t->kind == IR::Temp::PhysicalRegister) {
- switch (t->type) {
- case IR::DoubleType:
- as->moveDoubleToInts((FPRegisterID) t->index, lowReg, highReg);
- break;
- case IR::UInt32Type: {
- RegisterID srcReg = (RegisterID) t->index;
- Jump intRange = as->branch32(JITAssembler::GreaterThanOrEqual, srcReg, TrustedImm32(0));
- as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister);
- as->moveDoubleToInts(TargetPlatform::FPGpr0, lowReg, highReg);
- Jump done = as->jump();
- intRange.link(as);
- as->move(srcReg, lowReg);
- as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg);
- done.link(as);
- } break;
- case IR::SInt32Type:
- as->move((RegisterID) t->index, lowReg);
- as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg);
- break;
- case IR::BoolType:
- as->move((RegisterID) t->index, lowReg);
- as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Boolean)), highReg);
- break;
- default:
- Q_UNREACHABLE();
- }
- } else {
- Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, t);
- as->load32(addr, lowReg);
- addr.offset += 4;
- as->load32(addr, highReg);
- }
- }
-
- static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal)
- {
- as->move(TrustedImm32(retVal.value()), TargetPlatform::LowReturnValueRegister);
- as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(as);
- Q_UNUSED(temp);
- Q_UNUSED(dest);
- Q_UNUSED(argumentNumber);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(as);
- Q_UNUSED(al);
- Q_UNUSED(dest);
- Q_UNUSED(argumentNumber);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(as);
- Q_UNUSED(c);
- Q_UNUSED(dest);
- Q_UNUSED(argumentNumber);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(as);
- Q_UNUSED(expr);
- Q_UNUSED(dest);
- Q_UNUSED(argumentNumber);
- }
-
- static void zeroRegister(JITAssembler *as, RegisterID reg)
- {
- as->move(TrustedImm32(0), reg);
- }
-
- static void zeroStackSlot(JITAssembler *as, int slot)
- {
- as->poke(TrustedImm32(0), slot);
- }
-
- static void generateCJumpOnUndefined(JITAssembler *as,
- RelationalCondition cond, IR::Expr *right,
- RegisterID scratchRegister, RegisterID tagRegister,
- IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock)
- {
- Pointer tagAddr = as->loadAddressForReading(scratchRegister, right);
- as->load32(tagAddr, tagRegister);
- Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0));
- as->addPatch(falseBlock, j);
-
- tagAddr.offset += 4;
- as->load32(tagAddr, tagRegister);
- const TrustedImm32 tag(QV4::Value::Managed_Type_Internal);
- Q_ASSERT(nextBlock == as->nextBlock());
- Q_UNUSED(nextBlock);
- as->generateCJumpOnCompare(cond, tagRegister, tag, currentBlock, trueBlock, falseBlock);
- }
-
- static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target)
- {
- Q_ASSERT(source->type == IR::VarType);
- // load the tag:
- Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source);
- Pointer tagAddr = addr;
- tagAddr.offset += 4;
- as->load32(tagAddr, TargetPlatform::ReturnValueRegister);
-
- // check if it's an int32:
- Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister,
- TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)));
- IR::Temp *targetTemp = target->asTemp();
- if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
- as->load32(addr, TargetPlatform::ReturnValueRegister);
- WriteBarrier::Type barrier;
- Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier);
- as->store32(TargetPlatform::ReturnValueRegister, targetAddr);
- targetAddr.offset += 4;
- as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)), targetAddr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, targetAddr);
- } else {
- as->load32(addr, (RegisterID) targetTemp->index);
- }
- Jump intDone = as->jump();
-
- // not an int:
- fallback.link(as);
- generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt,
- as->loadAddressForReading(TargetPlatform::ScratchRegister, source));
- as->storeInt32(TargetPlatform::ReturnValueRegister, target);
-
- intDone.link(as);
- }
-
- static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier)
- {
- as->store32(registerWithPtr, destAddr);
- destAddr.offset += 4;
- as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr);
- if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, destAddr);
- }
-
- static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister)
- {
- as->and32(TrustedImm32(Value::NotDouble_Mask), tagOrValueRegister);
- return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister,
- TrustedImm32(Value::NotDouble_Mask));
- }
-
- static void initializeLocalVariables(JITAssembler *as, int localsCount)
- {
- as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister);
- as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister);
- Label loop = as->label();
- as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
- as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister);
- as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
- as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister);
- Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister);
- jump.linkTo(loop, as);
- }
-
- static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister)
- {
- as->and32(TrustedImm32(Value::NotDouble_Mask), tagRegister);
- Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(Value::NotDouble_Mask));
- return isNoDbl;
- }
-};
-
-template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform>
-struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 8>
-{
- using RegisterID = typename JITAssembler::RegisterID;
- using FPRegisterID = typename JITAssembler::FPRegisterID;
- using Address = typename JITAssembler::Address;
- using TrustedImm32 = typename JITAssembler::TrustedImm32;
- using TrustedImm64 = typename JITAssembler::TrustedImm64;
- using Pointer = typename JITAssembler::Pointer;
- using RelationalCondition = typename JITAssembler::RelationalCondition;
- using ResultCondition = typename JITAssembler::ResultCondition;
- using BranchTruncateType = typename JITAssembler::BranchTruncateType;
- using Jump = typename JITAssembler::Jump;
- using Label = typename JITAssembler::Label;
- using ValueTypeInternal = Value::ValueTypeInternal_64;
- using TargetPrimitive = TargetPrimitive64;
-
- static void emitSetGrayBit(JITAssembler *as, RegisterID base)
- {
- bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister);
-
- as->push(TargetPlatform::EngineRegister); // free up one register for work
-
- RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister;
- as->move(base, grayBitmap);
- Q_ASSERT(base != grayBitmap);
- as->urshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap);
- as->lshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap);
- Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0);
-
- RegisterID index = base;
- as->move(base, index);
- as->sub64(grayBitmap, index);
- as->urshift64(TrustedImm32(Chunk::SlotSizeShift), index);
- RegisterID grayIndex = TargetPlatform::EngineRegister;
- as->move(index, grayIndex);
- as->urshift64(TrustedImm32(Chunk::BitShift), grayIndex);
- as->lshift64(TrustedImm32(3), grayIndex); // 8 bytes per quintptr
- as->add64(grayIndex, grayBitmap);
- as->and64(TrustedImm32(Chunk::Bits - 1), index);
-
- RegisterID bit = TargetPlatform::EngineRegister;
- as->move(TrustedImm32(1), bit);
- as->lshift64(index, bit);
-
- as->load64(Pointer(grayBitmap, 0), index);
- as->or64(bit, index);
- as->store64(index, Pointer(grayBitmap, 0));
-
- as->pop(TargetPlatform::EngineRegister);
- }
+#define JIT_STRINGIFYx(s) #s
+#define JIT_STRINGIFY(s) JIT_STRINGIFYx(s)
-#if WRITEBARRIER(none)
- static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {}
-#endif
-
- static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest)
- {
- as->load64(addr, TargetPlatform::ReturnValueRegister);
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest);
- }
-
- static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier)
- {
- as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister);
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- as->store64(TargetPlatform::ReturnValueRegister, addr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, addr);
- }
-
- static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target)
- {
- as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister);
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- WriteBarrier::Type barrier;
- Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier);
- as->store64(TargetPlatform::ReturnValueRegister, ptr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, ptr);
- }
-
- static void storeReturnValue(JITAssembler *as, FPRegisterID dest)
- {
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest);
- }
-
- static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier)
- {
- as->store64(TargetPlatform::ReturnValueRegister, dest);
- if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, dest);
- }
-
- static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t)
- {
- if (t->kind == IR::Temp::PhysicalRegister) {
- if (t->type == IR::DoubleType) {
- as->moveDoubleTo64((FPRegisterID) t->index,
- TargetPlatform::ReturnValueRegister);
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- } else if (t->type == IR::UInt32Type) {
- RegisterID srcReg = (RegisterID) t->index;
- Jump intRange = as->branch32(RelationalCondition::GreaterThanOrEqual, srcReg, TrustedImm32(0));
- as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister);
- as->moveDoubleTo64(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister);
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- Jump done = as->jump();
- intRange.link(as);
- as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister);
- quint64 tag = quint64(QV4::Value::ValueTypeInternal_64::Integer);
- as->or64(TrustedImm64(tag << 32),
- TargetPlatform::ReturnValueRegister);
- done.link(as);
- } else {
- as->zeroExtend32ToPtr((RegisterID) t->index, TargetPlatform::ReturnValueRegister);
- quint64 tag;
- switch (t->type) {
- case IR::SInt32Type:
- tag = quint64(QV4::Value::ValueTypeInternal_64::Integer);
- break;
- case IR::BoolType:
- tag = quint64(QV4::Value::ValueTypeInternal_64::Boolean);
- break;
- default:
- tag = 31337; // bogus value
- Q_UNREACHABLE();
- }
- as->or64(TrustedImm64(tag << 32),
- TargetPlatform::ReturnValueRegister);
- }
- } else {
- as->copyValue(TargetPlatform::ReturnValueRegister, t, WriteBarrier::NoBarrier);
- }
- }
-
- static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal)
- {
- as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister);
- }
-
- static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier)
- {
- as->store64(TrustedImm64(value.rawValue()), destination);
- if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, destination);
- }
-
- template <typename Source, typename Destination>
- static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier)
- {
- // Use ReturnValueRegister as "scratch" register because loadArgument
- // and storeArgument are functions that may need a scratch register themselves.
- loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0);
- as->storeReturnValue(destination, barrier);
- }
-
- static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target)
- {
- Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double));
- int64_t i;
- memcpy(&i, &c->value, sizeof(double));
- as->move(TrustedImm64(i), TargetPlatform::ReturnValueRegister);
- as->move64ToDouble(TargetPlatform::ReturnValueRegister, target);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
- as->load64(addressOfValue, dest);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- if (temp) {
- Pointer addr = as->loadTempAddress(temp);
- as->load64(addr, dest);
- } else {
- auto undefined = TargetPrimitive::undefinedValue();
- as->move(TrustedImm64(undefined.rawValue()), dest);
- }
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- if (al) {
- Pointer addr = as->loadArgLocalAddressForReading(dest, al);
- as->load64(addr, dest);
- } else {
- auto undefined = TargetPrimitive::undefinedValue();
- as->move(TrustedImm64(undefined.rawValue()), dest);
- }
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- auto v = convertToValue<TargetPrimitive64>(c);
- as->move(TrustedImm64(v.rawValue()), dest);
- }
-
- static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- if (!expr) {
- auto undefined = TargetPrimitive::undefinedValue();
- as->move(TrustedImm64(undefined.rawValue()), dest);
- } else if (IR::Temp *t = expr->asTemp()){
- loadArgumentInRegister(as, t, dest, argumentNumber);
- } else if (IR::ArgLocal *al = expr->asArgLocal()) {
- loadArgumentInRegister(as, al, dest, argumentNumber);
- } else if (IR::Const *c = expr->asConst()) {
- loadArgumentInRegister(as, c, dest, argumentNumber);
- } else {
- Q_ASSERT(!"unimplemented expression type in loadArgument");
- }
- }
-
- static void zeroRegister(JITAssembler *as, RegisterID reg)
- {
- as->move(TrustedImm64(0), reg);
- }
-
- static void zeroStackSlot(JITAssembler *as, int slot)
- {
- as->store64(TrustedImm64(0), as->addressForPoke(slot));
- }
-
- static void generateCJumpOnCompare(JITAssembler *as,
- RelationalCondition cond,
- RegisterID left,
- TrustedImm64 right,
- IR::BasicBlock *nextBlock,
- IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
- {
- if (trueBlock == nextBlock) {
- Jump target = as->branch64(as->invert(cond), left, right);
- as->addPatch(falseBlock, target);
- } else {
- Jump target = as->branch64(cond, left, right);
- as->addPatch(trueBlock, target);
- as->jumpToBlock(currentBlock, falseBlock);
- }
- }
-
- static void generateCJumpOnUndefined(JITAssembler *as,
- RelationalCondition cond, IR::Expr *right,
- RegisterID scratchRegister, RegisterID tagRegister,
- IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock)
- {
- Pointer addr = as->loadAddressForReading(scratchRegister, right);
- as->load64(addr, tagRegister);
- const TrustedImm64 tag(0);
- generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock);
- }
-
- static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target)
- {
- Q_ASSERT(source->type == IR::VarType);
- Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source);
- as->load64(addr, TargetPlatform::ScratchRegister);
- as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister);
-
- // check if it's integer convertible
- as->urshift64(TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), TargetPlatform::ScratchRegister);
- Jump isIntConvertible = as->branch32(RelationalCondition::Equal, TargetPlatform::ScratchRegister, TrustedImm32(3));
-
- // nope, not integer convertible, so check for a double:
- as->urshift64(TrustedImm32(
- QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift),
- TargetPlatform::ScratchRegister);
- Jump fallback = as->branch32(RelationalCondition::GreaterThan, TargetPlatform::ScratchRegister, TrustedImm32(0));
-
- // it's a double
- as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister);
- as->move64ToDouble(TargetPlatform::ReturnValueRegister, TargetPlatform::FPGpr0);
- Jump success =
- as->branchTruncateDoubleToInt32(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister,
- BranchTruncateType::BranchIfTruncateSuccessful);
-
- // not an int:
- fallback.link(as);
- generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt,
- as->loadAddressForReading(TargetPlatform::ScratchRegister, source));
-
-
- isIntConvertible.link(as);
- success.link(as);
- IR::Temp *targetTemp = target->asTemp();
- if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
- WriteBarrier::Type barrier;
- Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier);
- as->store32(TargetPlatform::ReturnValueRegister, targetAddr);
- targetAddr.offset += 4;
- as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_64::Integer)), targetAddr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, targetAddr);
- } else {
- as->storeInt32(TargetPlatform::ReturnValueRegister, target);
- }
- }
-
- static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier)
- {
- as->store64(registerWithPtr, destAddr);
- if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier)
- emitWriteBarrier(as, destAddr);
- }
-
- static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister)
- {
- as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister);
- return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister,
- TrustedImm32(0));
- }
-
- static void initializeLocalVariables(JITAssembler *as, int localsCount)
- {
- as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister);
- as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister);
- Label loop = as->label();
- as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister));
- as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister);
- Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister);
- jump.linkTo(loop, as);
- }
-
- static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister)
- {
- as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagRegister);
- Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(0));
- return isNoDbl;
- }
-};
-
-template <typename TargetConfiguration>
-class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform
-{
- Q_DISABLE_COPY(Assembler)
+#define IN_JIT_GENERATE_RUNTIME_CALL(function, destination) \
+ callRuntime(JIT_STRINGIFY(function), \
+ reinterpret_cast<void *>(&function), \
+ destination)
+#define JIT_GENERATE_RUNTIME_CALL(function, destination) \
+ as->IN_JIT_GENERATE_RUNTIME_CALL(function, destination)
+class Assembler {
public:
- Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator);
-
- using MacroAssembler = typename TargetConfiguration::MacroAssembler;
- using RegisterID = typename MacroAssembler::RegisterID;
- using FPRegisterID = typename MacroAssembler::FPRegisterID;
- using Address = typename MacroAssembler::Address;
- using Label = typename MacroAssembler::Label;
- using Jump = typename MacroAssembler::Jump;
- using DataLabelPtr = typename MacroAssembler::DataLabelPtr;
- using TrustedImm32 = typename MacroAssembler::TrustedImm32;
- using TrustedImm64 = typename MacroAssembler::TrustedImm64;
- using TrustedImmPtr = typename MacroAssembler::TrustedImmPtr;
- using RelationalCondition = typename MacroAssembler::RelationalCondition;
- using typename MacroAssembler::DoubleCondition;
- using MacroAssembler::label;
- using MacroAssembler::move;
- using MacroAssembler::jump;
- using MacroAssembler::add32;
- using MacroAssembler::and32;
- using MacroAssembler::store32;
- using MacroAssembler::loadPtr;
- using MacroAssembler::load32;
- using MacroAssembler::branch32;
- using MacroAssembler::subDouble;
- using MacroAssembler::subPtr;
- using MacroAssembler::addPtr;
- using MacroAssembler::call;
- using MacroAssembler::poke;
- using MacroAssembler::branchTruncateDoubleToUint32;
- using MacroAssembler::or32;
- using MacroAssembler::moveDouble;
- using MacroAssembler::convertUInt32ToDouble;
- using MacroAssembler::invert;
- using MacroAssembler::convertInt32ToDouble;
- using MacroAssembler::rshift32;
- using MacroAssembler::storePtr;
- using MacroAssembler::ret;
-
- using JITTargetPlatform = typename TargetConfiguration::Platform;
- using JITTargetPlatform::RegisterArgumentCount;
- using JITTargetPlatform::StackSpaceAllocatedUponFunctionEntry;
- using JITTargetPlatform::RegisterSize;
- using JITTargetPlatform::StackAlignment;
- using JITTargetPlatform::ReturnValueRegister;
- using JITTargetPlatform::StackPointerRegister;
- using JITTargetPlatform::ScratchRegister;
- using JITTargetPlatform::EngineRegister;
- using JITTargetPlatform::StackShadowSpace;
- using JITTargetPlatform::registerForArgument;
- using JITTargetPlatform::FPGpr0;
- using JITTargetPlatform::platformEnterStandardStackFrame;
- using JITTargetPlatform::platformFinishEnteringStandardStackFrame;
- using JITTargetPlatform::platformLeaveStandardStackFrame;
-
- static qint32 targetStructureOffset(qint32 hostOffset)
- {
- Q_ASSERT(hostOffset % QT_POINTER_SIZE == 0);
- return (hostOffset * RegisterSize) / QT_POINTER_SIZE;
- }
-
- struct LookupCall {
- Address addr;
- uint getterSetterOffset;
-
- LookupCall(const Address &addr, uint getterSetterOffset)
- : addr(addr)
- , getterSetterOffset(getterSetterOffset)
- {}
- };
-
- struct RuntimeCall {
- Address addr;
-
- inline RuntimeCall(Runtime::RuntimeMethods method = Runtime::InvalidRuntimeMethod);
- bool isValid() const { return addr.offset >= 0; }
- };
-
- // Explicit type to allow distinguishing between
- // pushing an address itself or the value it points
- // to onto the stack when calling functions.
- struct Pointer : public Address
- {
- explicit Pointer(const Address& addr)
- : Address(addr)
- {}
- explicit Pointer(RegisterID reg, int32_t offset)
- : Address(reg, offset)
- {}
- };
-
- using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>;
- using ValueTypeInternal = typename RegisterSizeDependentOps::ValueTypeInternal;
- using TargetPrimitive = typename RegisterSizeDependentOps::TargetPrimitive;
-
- // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage
- // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned
- // by the garbage collector, so if any JS object needs to be retained, it should be put on the
- // JS stack.
- //
- // The "saved reg arg X" are on the C++ stack is used to store values in registers that need to
- // be passed by reference to native functions. It is fine to use the C++ stack, because only
- // non-object values can be stored in registers.
- //
- // Stack layout for the C++ stack:
- // return address
- // old FP <- FP
- // callee saved reg n
- // ...
- // callee saved reg 0
- // saved reg arg n
- // ...
- // saved reg arg 0 <- SP
- //
- // Stack layout for the JS stack:
- // function call argument n <- LocalsRegister
- // ...
- // function call argument 0
- // local 0
- // ...
- // local n
- class StackLayout
- {
- public:
- StackLayout(IR::Function *function, int maxArgCountForBuiltins, int normalRegistersToSave, int fpRegistersToSave)
- : normalRegistersToSave(normalRegistersToSave)
- , fpRegistersToSave(fpRegistersToSave)
- , maxOutgoingArgumentCount(function->maxNumberOfArguments)
- , localCount(function->tempCount)
- , savedRegCount(maxArgCountForBuiltins)
- {
-#if 0 // debug code
- qDebug("calleeSavedRegCount.....: %d",calleeSavedRegCount);
- qDebug("maxOutgoingArgumentCount: %d",maxOutgoingArgumentCount);
- qDebug("localCount..............: %d",localCount);
- qDebug("savedConstCount.........: %d",savedRegCount);
- for (int i = 0; i < maxOutgoingArgumentCount; ++i)
- qDebug("argumentAddressForCall(%d) = 0x%x / -0x%x", i,
- argumentAddressForCall(i).offset, -argumentAddressForCall(i).offset);
- for (int i = 0; i < localCount; ++i)
- qDebug("local(%d) = 0x%x / -0x%x", i, stackSlotPointer(i).offset,
- -stackSlotPointer(i).offset);
- qDebug("savedReg(0) = 0x%x / -0x%x", savedRegPointer(0).offset, -savedRegPointer(0).offset);
- qDebug("savedReg(1) = 0x%x / -0x%x", savedRegPointer(1).offset, -savedRegPointer(1).offset);
- qDebug("savedReg(2) = 0x%x / -0x%x", savedRegPointer(2).offset, -savedRegPointer(2).offset);
- qDebug("savedReg(3) = 0x%x / -0x%x", savedRegPointer(3).offset, -savedRegPointer(3).offset);
- qDebug("savedReg(4) = 0x%x / -0x%x", savedRegPointer(4).offset, -savedRegPointer(4).offset);
- qDebug("savedReg(5) = 0x%x / -0x%x", savedRegPointer(5).offset, -savedRegPointer(5).offset);
-
- qDebug("callDataAddress(0) = 0x%x", callDataAddress(0).offset);
-#endif
- }
-
- int calculateStackFrameSize() const
- {
- // sp was aligned before executing the call instruction. So, calculate all contents
- // that were saved after that aligned stack...:
- const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry
- + RegisterSize; // saved FramePointerRegister
-
- // ... then calculate the stuff we want to store ...:
- int frameSize = RegisterSize * normalRegistersToSave + sizeof(double) * fpRegistersToSave;
- frameSize += savedRegCount * sizeof(QV4::Value); // (these get written out as Values, not as native registers)
-
- Q_ASSERT(frameSize + stackSpaceAllocatedOtherwise < INT_MAX);
- // .. then align that chunk ..:
- frameSize = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise));
- // ... which now holds our frame size + the extra stuff that was pushed due to the call.
- // So subtract that extra stuff, and we have our frame size:
- frameSize -= stackSpaceAllocatedOtherwise;
-
- return frameSize;
- }
-
- /// \return the stack frame size in number of Value items.
- int calculateJSStackFrameSize() const
- {
- return (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1;
- }
-
- Address stackSlotPointer(int idx) const
- {
- Q_ASSERT(idx >= 0);
- Q_ASSERT(idx < localCount);
-
- Pointer addr = callDataAddress(0);
- addr.offset -= sizeof(QV4::Value) * (idx + 1);
- return addr;
- }
-
- // Some run-time functions take (Value* args, int argc). This function is for populating
- // the args.
- Pointer argumentAddressForCall(int argument) const
- {
- Q_ASSERT(argument >= 0);
- Q_ASSERT(argument < maxOutgoingArgumentCount);
-
- const int index = maxOutgoingArgumentCount - argument;
- return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index));
- }
-
- Pointer callDataAddress(int offset = 0) const {
- return Pointer(Assembler::LocalsRegister, offset - (sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1)));
- }
-
- Address savedRegPointer(int offset) const
- {
- Q_ASSERT(offset >= 0);
- Q_ASSERT(offset < savedRegCount);
-
- // Get the address of the bottom-most element of our frame:
- Address ptr(Assembler::FramePointerRegister, -calculateStackFrameSize());
- // This now is the element with offset 0. So:
- ptr.offset += offset * sizeof(QV4::Value);
- // and we're done!
- return ptr;
- }
-
- private:
- int normalRegistersToSave;
- int fpRegistersToSave;
-
- /// arg count for calls to JS functions
- int maxOutgoingArgumentCount;
-
- /// the number of spill slots needed by this function
- int localCount;
-
- /// used by built-ins to save arguments (e.g. constants) to the stack when they need to be
- /// passed by reference.
- int savedRegCount;
+ enum CallResultDestination {
+ IgnoreResult,
+ ResultInAccumulator,
};
- struct VoidType { VoidType() {} };
- static const VoidType Void;
+ Assembler(const Value* constantTable);
+ ~Assembler();
+
+ // codegen infrastructure
+ void generatePrologue();
+ void generateEpilogue();
+ void link(Function *function);
+ void addLabel(int offset);
+
+ // loads/stores/moves
+ void loadConst(int constIndex);
+ void copyConst(int constIndex, int destReg);
+ void loadReg(int reg);
+ void storeReg(int reg);
+ void loadString(int stringId);
+ void loadValue(ReturnedValue value);
+
+ // numeric ops
+ void unot();
+ void toNumber();
+ void uminus();
+ void ucompl();
+ void inc();
+ void dec();
+ void add(int lhs);
+ void bitAnd(int lhs);
+ void bitOr(int lhs);
+ void bitXor(int lhs);
+ void ushr(int lhs);
+ void shr(int lhs);
+ void shl(int lhs);
+ void bitAndConst(int rhs);
+ void bitOrConst(int rhs);
+ void bitXorConst(int rhs);
+ void ushrConst(int rhs);
+ void shrConst(int rhs);
+ void shlConst(int rhs);
+ void mul(int lhs);
+ void div(int lhs);
+ void mod(int lhs);
+ void sub(int lhs);
+
+ // comparissons
+ void cmpeqNull();
+ void cmpneNull();
+ void cmpeqInt(int lhs);
+ void cmpneInt(int lhs);
+ void cmpeq(int lhs);
+ void cmpne(int lhs);
+ void cmpgt(int lhs);
+ void cmpge(int lhs);
+ void cmplt(int lhs);
+ void cmple(int lhs);
+ void cmpStrictEqual(int lhs);
+ void cmpStrictNotEqual(int lhs);
+
+ // jumps
+ void jump(int offset);
+ void jumpTrue(int offset);
+ void jumpFalse(int offset);
+ void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset);
+ void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset);
+
+ // stuff for runtime calls
+ void prepareCallWithArgCount(int argc);
+ void storeInstructionPointer(int instructionOffset);
+ void passAccumulatorAsArg(int arg);
+ void passFunctionAsArg(int arg);
+ void passEngineAsArg(int arg);
+ void passRegAsArg(int reg, int arg);
+ void passCppFrameAsArg(int arg);
+ void passInt32AsArg(int value, int arg);
+ void callRuntime(const char *functionName, const void *funcPtr, Assembler::CallResultDestination dest);
+ void saveAccumulatorInFrame();
+
+ // exception/context stuff
+ void checkException();
+ void gotoCatchException();
+ void getException();
+ void setException();
+ void setExceptionHandler(int offset);
+ void clearExceptionHandler();
+ void pushCatchContext(int name, int reg);
+ void popContext(int reg);
+
+ // other stuff
+ void ret();
+
+protected:
+ void *d;
- typedef JSC::FunctionPtr FunctionPtr;
-
-#ifndef QT_NO_DEBUG
- struct CallInfo {
- Label label;
- const char* functionName;
- };
-#endif
- struct PointerToValue {
- PointerToValue(IR::Expr *value)
- : value(value)
- {}
- IR::Expr *value;
- };
- struct StringToIndex {
- explicit StringToIndex(const QString &string) : string(string) {}
- QString string;
- };
- struct Reference {
- Reference(IR::Expr *value) : value(value) {
- Q_ASSERT(value->asTemp() || value->asArgLocal());
- }
- IR::Expr *value;
- };
-
- void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall)
- {
- call(lookupCall.addr);
- }
-
- void callAbsolute(const char *functionName, const RuntimeCall &runtimeCall)
- {
- call(runtimeCall.addr);
#ifndef QT_NO_DEBUG
- // the code below is to get proper function names in the disassembly
- CallInfo info;
- info.functionName = functionName;
- info.label = label();
- _callInfos.append(info);
-#else
- Q_UNUSED(functionName)
+ enum { NoCall = -1 };
+ int remainingArgcForCall = NoCall;
#endif
- }
-
- void registerBlock(IR::BasicBlock*, IR::BasicBlock *nextBlock);
- IR::BasicBlock *nextBlock() const { return _nextBlock; }
- void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target);
- void addPatch(IR::BasicBlock* targetBlock, Jump targetJump);
- void addPatch(DataLabelPtr patch, Label target);
- void addPatch(DataLabelPtr patch, IR::BasicBlock *target);
- void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right,
- IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock);
- void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right,
- IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock);
- void generateCJumpOnUndefined(RelationalCondition cond, IR::Expr *right,
- RegisterID scratchRegister, RegisterID tagRegister,
- IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
- {
- RegisterSizeDependentOps::generateCJumpOnUndefined(this, cond, right, scratchRegister, tagRegister,
- _nextBlock, currentBlock, trueBlock, falseBlock);
- }
-
- Jump generateIsDoubleCheck(RegisterID tagOrValueRegister)
- {
- return RegisterSizeDependentOps::generateIsDoubleCheck(this, tagOrValueRegister);
- }
-
- Jump genTryDoubleConversion(IR::Expr *src, FPRegisterID dest);
- Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right);
- Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right);
-
- Pointer loadAddressForWriting(RegisterID tmp, IR::Expr *t, WriteBarrier::Type *barrier);
- Pointer loadAddressForReading(RegisterID tmp, IR::Expr *t) {
- return loadAddressForWriting(tmp, t, 0);
- }
-
- Pointer loadTempAddress(IR::Temp *t);
- Pointer loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier);
- Pointer loadArgLocalAddressForReading(RegisterID baseReg, IR::ArgLocal *al) {
- return loadArgLocalAddressForWriting(baseReg, al, 0);
- }
- Pointer loadStringAddress(RegisterID reg, const QString &string);
- Address loadConstant(IR::Const *c, RegisterID baseReg);
- Address loadConstant(const TargetPrimitive &v, RegisterID baseReg);
- void loadStringRef(RegisterID reg, const QString &string);
- Pointer stackSlotPointer(IR::Temp *t) const
- {
- Q_ASSERT(t->kind == IR::Temp::StackSlot);
-
- return Pointer(_stackLayout->stackSlotPointer(t->index));
- }
-
- template <int argumentNumber>
- void saveOutRegister(PointerToValue arg)
- {
- if (!arg.value)
- return;
- if (IR::Temp *t = arg.value->asTemp()) {
- if (t->kind == IR::Temp::PhysicalRegister) {
- Pointer addr(_stackLayout->savedRegPointer(argumentNumber));
- switch (t->type) {
- case IR::BoolType:
- storeBool((RegisterID) t->index, addr, WriteBarrier::NoBarrier);
- break;
- case IR::SInt32Type:
- storeInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier);
- break;
- case IR::UInt32Type:
- storeUInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier);
- break;
- case IR::DoubleType:
- storeDouble((FPRegisterID) t->index, addr, WriteBarrier::NoBarrier);
- break;
- default:
- Q_UNIMPLEMENTED();
- }
- }
- }
- }
-
- template <int, typename ArgType>
- void saveOutRegister(ArgType)
- {}
-
- void loadArgumentInRegister(RegisterID source, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- move(source, dest);
- }
-
- void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
- addPtr(TrustedImm32(ptr.offset), ptr.base, dest);
- }
-
- void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber)
- {
- if (!temp.value) {
- RegisterSizeDependentOps::zeroRegister(this, dest);
- } else {
- Pointer addr = toAddress(dest, temp.value, argumentNumber, 0);
- loadArgumentInRegister(addr, dest, argumentNumber);
- }
- }
- void loadArgumentInRegister(StringToIndex temp, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
- loadStringRef(dest, temp.string);
- }
-
- void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber)
- {
- Q_ASSERT(temp.value);
- Pointer addr = loadAddressForReading(dest, temp.value);
- loadArgumentInRegister(addr, dest, argumentNumber);
- }
-
- void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber)
- {
- RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber);
- }
-
- void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber)
- {
- RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber);
- }
-
- void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber)
- {
- RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber);
- }
-
- void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber)
- {
- RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber);
- }
-
- void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- RegisterSizeDependentOps::zeroRegister(this, dest);
- if (imm32.m_value)
- move(imm32, dest);
- }
-
- void storeReturnValue(RegisterID dest, WriteBarrier::Type barrier = WriteBarrier::NoBarrier)
- {
- Q_UNUSED(barrier);
- Q_ASSERT(barrier == WriteBarrier::NoBarrier);
- move(ReturnValueRegister, dest);
- }
-
- void storeUInt32ReturnValue(RegisterID dest)
- {
- subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister);
- Pointer tmp(StackPointerRegister, 0);
- storeReturnValue(tmp, WriteBarrier::NoBarrier);
- toUInt32Register(tmp, dest);
- addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister);
- }
-
- void storeReturnValue(FPRegisterID dest)
- {
- RegisterSizeDependentOps::storeReturnValue(this, dest);
- }
-
- void storeReturnValue(const Pointer &dest, WriteBarrier::Type barrier)
- {
- RegisterSizeDependentOps::storeReturnValue(this, dest, barrier);
- }
-
- void storeReturnValue(IR::Expr *target)
- {
- if (!target)
- return;
-
- IR::Temp *temp = target->asTemp();
- if (temp && temp->kind == IR::Temp::PhysicalRegister) {
- if (temp->type == IR::DoubleType)
- storeReturnValue((FPRegisterID) temp->index);
- else if (temp->type == IR::UInt32Type)
- storeUInt32ReturnValue((RegisterID) temp->index);
- else
- storeReturnValue((RegisterID) temp->index);
- return;
- } else {
- WriteBarrier::Type barrier;
- Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier);
- storeReturnValue(addr, barrier);
- }
- }
+ int argcOnStackForCall = 0;
- void storeReturnValue(VoidType)
- {
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(RegisterID reg, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- poke(reg, StackSlot);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(TrustedImm32 value, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- poke(value, StackSlot);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(const Pointer& ptr, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister);
- poke(ScratchRegister, StackSlot);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(PointerToValue temp, int argumentNumber)
- {
- if (temp.value) {
- Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber, 0);
- loadArgumentOnStack<StackSlot>(ptr, argumentNumber);
- } else {
- RegisterSizeDependentOps::zeroStackSlot(this, StackSlot);
- }
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(StringToIndex temp, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
- loadStringRef(ScratchRegister, temp.string);
- poke(ScratchRegister, StackSlot);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(Reference temp, int argumentNumber)
- {
- Q_ASSERT (temp.value);
-
- Pointer ptr = loadAddressForReading(ScratchRegister, temp.value);
- loadArgumentOnStack<StackSlot>(ptr, argumentNumber);
- }
-
- void loadDouble(IR::Expr *source, FPRegisterID dest)
- {
- IR::Temp *sourceTemp = source->asTemp();
- if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) {
- moveDouble((FPRegisterID) sourceTemp->index, dest);
- return;
- }
- Pointer ptr = loadAddressForReading(ScratchRegister, source);
- loadDouble(ptr, dest);
- }
-
- void storeDouble(FPRegisterID source, IR::Expr* target)
- {
- IR::Temp *targetTemp = target->asTemp();
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- moveDouble(source, (FPRegisterID) targetTemp->index);
- return;
- }
- RegisterSizeDependentOps::storeDouble(this, source, target);
- }
-
- void loadDouble(Address addr, FPRegisterID dest)
- {
- RegisterSizeDependentOps::loadDouble(this, addr, dest);
- }
-
- void storeDouble(FPRegisterID source, Address addr, WriteBarrier::Type barrier)
- {
- RegisterSizeDependentOps::storeDouble(this, source, addr, barrier);
- }
-
- template <typename Result, typename Source>
- void copyValue(Result result, Source source, WriteBarrier::Type barrier);
- template <typename Result>
- void copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier);
-
- // The scratch register is used to calculate the temp address for the source.
- void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister, WriteBarrier::Type barrier)
- {
- Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister);
- Q_ASSERT(target.base != scratchRegister);
- loadRawValue(loadAddressForReading(scratchRegister, source), FPGpr0);
- storeRawValue(FPGpr0, target, barrier);
- }
-
- // The scratch register is used to calculate the temp address for the source.
- void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister)
- {
- loadRawValue(source, fpScratchRegister);
- WriteBarrier::Type barrier;
- Pointer dest = loadAddressForWriting(scratchRegister, target, &barrier);
- storeRawValue(fpScratchRegister, dest, barrier);
- }
-
- void loadRawValue(Pointer source, FPRegisterID dest)
- {
- TargetConfiguration::MacroAssembler::loadDouble(source, dest);
- }
-
- void storeRawValue(FPRegisterID source, Pointer dest, WriteBarrier::Type barrier)
- {
- TargetConfiguration::MacroAssembler::storeDouble(source, dest);
- if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier)
- RegisterSizeDependentOps::emitWriteBarrier(this, dest);
- }
-
- void storeValue(TargetPrimitive value, Address destination, WriteBarrier::Type barrier)
- {
- RegisterSizeDependentOps::storeValue(this, value, destination, barrier);
- }
-
- void storeValue(TargetPrimitive value, IR::Expr* temp);
-
- void emitWriteBarrier(Address addr, WriteBarrier::Type barrier) {
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- RegisterSizeDependentOps::emitWriteBarrier(this, addr);
- }
-
- void enterStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave);
- void leaveStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave);
-
- void checkException() {
- this->load8(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, hasException))), ScratchRegister);
- Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0));
- if (catchBlock)
- addPatch(catchBlock, exceptionThrown);
- else
- exceptionPropagationJumps.append(exceptionThrown);
- }
- void jumpToExceptionHandler() {
- Jump exceptionThrown = jump();
- if (catchBlock)
- addPatch(catchBlock, exceptionThrown);
- else
- exceptionPropagationJumps.append(exceptionThrown);
- }
-
- template <int argumentNumber, typename T>
- void loadArgumentOnStackOrRegister(const T &value)
- {
- if (argumentNumber < RegisterArgumentCount)
- loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber);
- else
- loadArgumentOnStack<argumentNumber - RegisterArgumentCount + (StackShadowSpace / RegisterSize)>(value, argumentNumber);
- }
-
- template <int argumentNumber>
- void loadArgumentOnStackOrRegister(const VoidType &value)
- {
- Q_UNUSED(value);
- }
-
- template <bool selectFirst, int First, int Second>
- struct Select
- {
- enum { Chosen = First };
- };
-
- template <int First, int Second>
- struct Select<false, First, Second>
- {
- enum { Chosen = Second };
- };
-
- template <int ArgumentIndex, typename Parameter>
- struct SizeOnStack
- {
- enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, RegisterSize, 0>::Chosen };
- };
-
- template <int ArgumentIndex>
- struct SizeOnStack<ArgumentIndex, VoidType>
- {
- enum { Size = 0 };
- };
-
- template <typename T> bool prepareCall(T &)
- { return true; }
-
- bool prepareCall(LookupCall &lookupCall)
- {
- // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details!
-
- // load the table from the context
- loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), ScratchRegister);
- loadPtr(Address(ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lookups))),
- lookupCall.addr.base);
- // pre-calculate the indirect address for the lookupCall table:
- if (lookupCall.addr.offset)
- addPtr(TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base);
- // store it as the first argument
- loadArgumentOnStackOrRegister<0>(lookupCall.addr.base);
- // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address
- lookupCall.addr.offset = lookupCall.getterSetterOffset;
- return false;
- }
-
- template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6)
- {
- int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size
- + SizeOnStack<1, Arg2>::Size
- + SizeOnStack<2, Arg3>::Size
- + SizeOnStack<3, Arg4>::Size
- + SizeOnStack<4, Arg5>::Size
- + SizeOnStack<5, Arg6>::Size
- + StackShadowSpace;
-
- if (stackSpaceNeeded) {
- Q_ASSERT(stackSpaceNeeded < (INT_MAX - StackAlignment));
- stackSpaceNeeded = static_cast<int>(WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded));
- subPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister);
- }
-
- // First save any arguments that reside in registers, because they could be overwritten
- // if that register is also used to pass arguments.
- saveOutRegister<5>(arg6);
- saveOutRegister<4>(arg5);
- saveOutRegister<3>(arg4);
- saveOutRegister<2>(arg3);
- saveOutRegister<1>(arg2);
- saveOutRegister<0>(arg1);
-
- loadArgumentOnStackOrRegister<5>(arg6);
- loadArgumentOnStackOrRegister<4>(arg5);
- loadArgumentOnStackOrRegister<3>(arg4);
- loadArgumentOnStackOrRegister<2>(arg3);
- loadArgumentOnStackOrRegister<1>(arg2);
-
- if (prepareCall(function))
- loadArgumentOnStackOrRegister<0>(arg1);
-
- if (JITTargetPlatform::gotRegister != -1)
- load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr
-
- callAbsolute(functionName, function);
-
- if (stackSpaceNeeded)
- addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister);
-
- if (needsExceptionCheck) {
- checkException();
- }
-
- storeReturnValue(r);
-
- }
-
- template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
- {
- generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType());
- }
-
- template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType());
- }
-
- template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType());
- }
-
- template <typename ArgRet, typename Callable, typename Arg1, typename Arg2>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2)
- {
- generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType());
- }
-
- template <typename ArgRet, typename Callable, typename Arg1>
- void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1)
- {
- generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType());
- }
-
- Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset, WriteBarrier::Type *barrier)
- {
- if (barrier)
- *barrier = WriteBarrier::NoBarrier;
- if (IR::Const *c = e->asConst()) {
- Address addr = _stackLayout->savedRegPointer(offset);
- Address tagAddr = addr;
- tagAddr.offset += 4;
-
- auto v = convertToValue<TargetPrimitive>(c);
- store32(TrustedImm32(v.value()), addr);
- store32(TrustedImm32(v.tag()), tagAddr);
- return Pointer(addr);
- }
-
- if (IR::Temp *t = e->asTemp())
- if (t->kind == IR::Temp::PhysicalRegister)
- return Pointer(_stackLayout->savedRegPointer(offset));
-
- return loadAddressForWriting(tmpReg, e, barrier);
- }
-
- void storeBool(RegisterID reg, Pointer addr, WriteBarrier::Type barrier)
- {
- store32(reg, addr);
- addr.offset += 4;
- store32(TrustedImm32(TargetPrimitive::fromBoolean(0).tag()), addr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- RegisterSizeDependentOps::emitWriteBarrier(this, addr);
- }
-
- void storeBool(RegisterID src, RegisterID dest)
- {
- move(src, dest);
- }
-
- void storeBool(RegisterID reg, IR::Expr *target)
- {
- if (IR::Temp *targetTemp = target->asTemp()) {
- if (targetTemp->kind == IR::Temp::PhysicalRegister) {
- move(reg, (RegisterID) targetTemp->index);
- return;
- }
- }
-
- WriteBarrier::Type barrier;
- Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier);
- storeBool(reg, addr, barrier);
- }
-
- void storeBool(bool value, IR::Expr *target) {
- TrustedImm32 trustedValue(value ? 1 : 0);
-
- if (IR::Temp *targetTemp = target->asTemp()) {
- if (targetTemp->kind == IR::Temp::PhysicalRegister) {
- move(trustedValue, (RegisterID) targetTemp->index);
- return;
- }
- }
-
- move(trustedValue, ScratchRegister);
- storeBool(ScratchRegister, target);
- }
-
- void storeInt32(RegisterID src, RegisterID dest)
- {
- move(src, dest);
- }
-
- void storeInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier)
- {
- store32(reg, addr);
- addr.offset += 4;
- store32(TrustedImm32(TargetPrimitive::fromInt32(0).tag()), addr);
- if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier)
- RegisterSizeDependentOps::emitWriteBarrier(this, addr);
- }
-
- void storeInt32(RegisterID reg, IR::Expr *target)
- {
- IR::Temp *targetTemp = target->asTemp();
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- move(reg, (RegisterID) targetTemp->index);
- } else {
- WriteBarrier::Type barrier;
- Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier);
- storeInt32(reg, addr, barrier);
- }
- }
-
- void storeUInt32(RegisterID src, RegisterID dest)
- {
- move(src, dest);
- }
-
- void storeUInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier)
- {
- // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register.
- Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0));
- convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister);
- storeDouble(FPGpr0, addr, barrier);
- Jump done = jump();
- intRange.link(this);
- storeInt32(reg, addr, barrier);
- done.link(this);
- }
-
- void storeUInt32(RegisterID reg, IR::Expr *target)
- {
- IR::Temp *targetTemp = target->asTemp();
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- move(reg, (RegisterID) targetTemp->index);
- } else {
- WriteBarrier::Type barrier;
- Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier);
- storeUInt32(reg, addr, barrier);
- }
- }
-
- FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0)
- {
- if (IR::Const *c = e->asConst()) {
- RegisterSizeDependentOps::loadDoubleConstant(this, c, target);
- return target;
- }
-
- if (IR::Temp *t = e->asTemp())
- if (t->kind == IR::Temp::PhysicalRegister)
- return (FPRegisterID) t->index;
-
- loadDouble(e, target);
- return target;
- }
-
- RegisterID toBoolRegister(IR::Expr *e, RegisterID scratchReg)
- {
- return toInt32Register(e, scratchReg);
- }
-
- RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg)
- {
- if (IR::Const *c = e->asConst()) {
- move(TrustedImm32(convertToValue<Primitive>(c).int_32()), scratchReg);
- return scratchReg;
- }
-
- if (IR::Temp *t = e->asTemp())
- if (t->kind == IR::Temp::PhysicalRegister)
- return (RegisterID) t->index;
-
- return toInt32Register(loadAddressForReading(scratchReg, e), scratchReg);
- }
-
- RegisterID toInt32Register(Pointer addr, RegisterID scratchReg)
- {
- load32(addr, scratchReg);
- return scratchReg;
- }
-
- RegisterID toUInt32Register(IR::Expr *e, RegisterID scratchReg)
- {
- if (IR::Const *c = e->asConst()) {
- move(TrustedImm32(unsigned(c->value)), scratchReg);
- return scratchReg;
- }
-
- if (IR::Temp *t = e->asTemp())
- if (t->kind == IR::Temp::PhysicalRegister)
- return (RegisterID) t->index;
-
- return toUInt32Register(loadAddressForReading(scratchReg, e), scratchReg);
- }
-
- RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg)
- {
- Q_ASSERT(addr.base != scratchReg);
-
- // The UInt32 representation in QV4::Value is really convoluted. See also storeUInt32.
- Pointer tagAddr = addr;
- tagAddr.offset += 4;
- load32(tagAddr, scratchReg);
- Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(quint32(ValueTypeInternal::Integer)));
-
- // it's not in signed int range, so load it as a double, and truncate it down
- loadDouble(addr, FPGpr0);
- Address inversionAddress = loadConstant(TargetPrimitive::fromDouble(double(INT_MAX) + 1), scratchReg);
- subDouble(inversionAddress, FPGpr0);
- Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg);
- canNeverHappen.link(this);
- or32(TrustedImm32(1 << 31), scratchReg);
- Jump done = jump();
-
- inIntRange.link(this);
- load32(addr, scratchReg);
-
- done.link(this);
- return scratchReg;
- }
-
- void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave);
-
- JSC::MacroAssemblerCodeRef link(int *codeSize);
-
- void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave);
- const StackLayout &stackLayout() const { return *_stackLayout.data(); }
- void initializeLocalVariables()
- {
- const int locals = _stackLayout->calculateJSStackFrameSize();
- if (locals <= 0)
- return;
- loadPtr(Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))), JITTargetPlatform::LocalsRegister);
- RegisterSizeDependentOps::initializeLocalVariables(this, locals);
- storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))));
- }
-
- Label exceptionReturnLabel;
- IR::BasicBlock * catchBlock;
- QVector<Jump> exceptionPropagationJumps;
private:
- QScopedPointer<const StackLayout> _stackLayout;
- IR::Function *_function;
- std::vector<Label> _addrs;
- std::vector<std::vector<Jump>> _patches;
-#ifndef QT_NO_DEBUG
- QVector<CallInfo> _callInfos;
-#endif
-
- struct DataLabelPatch {
- DataLabelPtr dataLabel;
- Label target;
- };
- std::vector<DataLabelPatch> _dataLabelPatches;
-
- std::vector<std::vector<DataLabelPtr>> _labelPatches;
- IR::BasicBlock *_nextBlock;
-
- QV4::ExecutableAllocator *_executableAllocator;
- QV4::Compiler::JSUnitGenerator *_jsGenerator;
+ typedef unsigned(*CmpFunc)(const Value&,const Value&);
+ void cmp(int cond, CmpFunc function, const char *functionName, int lhs);
+ void passAccumulatorAsArg_internal(int arg, bool push);
};
-template <typename TargetConfiguration>
-const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void;
-
-template <typename TargetConfiguration>
-template <typename Result, typename Source>
-void Assembler<TargetConfiguration>::copyValue(Result result, Source source, WriteBarrier::Type barrier)
-{
- RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier);
-}
-
-template <typename TargetConfiguration>
-template <typename Result>
-void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier)
-{
- if (source->type == IR::BoolType) {
- RegisterID reg = toInt32Register(source, ScratchRegister);
- storeBool(reg, result, barrier);
- } else if (source->type == IR::SInt32Type) {
- RegisterID reg = toInt32Register(source, ScratchRegister);
- storeInt32(reg, result, barrier);
- } else if (source->type == IR::UInt32Type) {
- RegisterID reg = toUInt32Register(source, ScratchRegister);
- storeUInt32(reg, result, barrier);
- } else if (source->type == IR::DoubleType) {
- storeDouble(toDoubleRegister(source), result, barrier);
- } else if (source->asTemp() || source->asArgLocal()) {
- RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier);
- } else if (IR::Const *c = source->asConst()) {
- auto v = convertToValue<TargetPrimitive>(c);
- storeValue(v, result, barrier);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-template <typename TargetConfiguration>
-inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(Runtime::RuntimeMethods method)
- : addr(Assembler::EngineRegister,
- method == Runtime::InvalidRuntimeMethod ? -1 : (Assembler<TargetConfiguration>::targetStructureOffset(offsetof(EngineBase, runtime) + Runtime::runtimeMethodOffset(method))))
-{
-}
-
-} // end of namespace JIT
-} // end of namespace QV4
+} // namespace JIT
+} // namespace QV4
QT_END_NAMESPACE
-#endif // ENABLE(ASSEMBLER)
-
-#endif // QV4ISEL_MASM_P_H
+#endif // QV4ASSEMBLER_P_H
diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp
deleted file mode 100644
index a1c65f644c..0000000000
--- a/src/qml/jit/qv4binop.cpp
+++ /dev/null
@@ -1,665 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qv4binop_p.h>
-#include <qv4assembler_p.h>
-
-#if ENABLE(ASSEMBLER)
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace JIT {
-
-template <typename JITAssembler>
-struct ArchitectureSpecificBinaryOperation
-{
- using FPRegisterID = typename JITAssembler::FPRegisterID;
-
- static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- Q_UNUSED(as);
- Q_UNUSED(lhs);
- Q_UNUSED(rhs);
- Q_UNUSED(targetReg);
- return false;
- }
- static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- Q_UNUSED(as);
- Q_UNUSED(lhs);
- Q_UNUSED(rhs);
- Q_UNUSED(targetReg);
- return false;
- }
- static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- Q_UNUSED(as);
- Q_UNUSED(lhs);
- Q_UNUSED(rhs);
- Q_UNUSED(targetReg);
- return false;
- }
- static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- Q_UNUSED(as);
- Q_UNUSED(lhs);
- Q_UNUSED(rhs);
- Q_UNUSED(targetReg);
- return false;
- }
-};
-
-#if CPU(X86)
-template <>
-struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>>
-{
- using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>;
- using FPRegisterID = JITAssembler::FPRegisterID;
- using Address = JITAssembler::Address;
-
- static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address]
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
- as->addDouble(addr, targetReg);
- return true;
- }
- if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address]
- if (t->kind != IR::Temp::PhysicalRegister) {
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->addDouble(as->loadTempAddress(t), targetReg);
- return true;
- }
- }
- return false;
- }
- static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address]
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
- as->mulDouble(addr, targetReg);
- return true;
- }
- if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address]
- if (t->kind != IR::Temp::PhysicalRegister) {
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->mulDouble(as->loadTempAddress(t), targetReg);
- return true;
- }
- }
- return false;
- }
- static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address]
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
- as->subDouble(addr, targetReg);
- return true;
- }
- if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address]
- if (t->kind != IR::Temp::PhysicalRegister) {
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->subDouble(as->loadTempAddress(t), targetReg);
- return true;
- }
- }
- return false;
- }
- static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg)
- {
- if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address]
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Address addr = as->loadConstant(c, JITAssembler::ScratchRegister);
- as->divDouble(addr, targetReg);
- return true;
- }
- if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address]
- if (t->kind != IR::Temp::PhysicalRegister) {
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->divDouble(as->loadTempAddress(t), targetReg);
- return true;
- }
- }
- return false;
- }
-};
-#endif
-
-#define OP(op) \
- { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
-#define OPCONTEXT(op) \
- { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
-
-#define INLINE_OP(op, memOp, immOp) \
- { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
-#define INLINE_OPCONTEXT(op, memOp, immOp) \
- { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
-
-#define NULL_OP \
- { 0, QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::InvalidRuntimeMethod, 0, 0, false }
-
-template <typename JITAssembler>
-const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = {
- NULL_OP, // OpInvalid
- NULL_OP, // OpIfTrue
- NULL_OP, // OpNot
- NULL_OP, // OpUMinus
- NULL_OP, // OpUPlus
- NULL_OP, // OpCompl
- NULL_OP, // OpIncrement
- NULL_OP, // OpDecrement
-
- INLINE_OP(bitAnd, &Binop<JITAssembler>::inline_and32, &Binop<JITAssembler>::inline_and32), // OpBitAnd
- INLINE_OP(bitOr, &Binop<JITAssembler>::inline_or32, &Binop<JITAssembler>::inline_or32), // OpBitOr
- INLINE_OP(bitXor, &Binop<JITAssembler>::inline_xor32, &Binop<JITAssembler>::inline_xor32), // OpBitXor
-
- INLINE_OPCONTEXT(add, &Binop<JITAssembler>::inline_add32, &Binop<JITAssembler>::inline_add32), // OpAdd
- INLINE_OP(sub, &Binop<JITAssembler>::inline_sub32, &Binop<JITAssembler>::inline_sub32), // OpSub
- INLINE_OP(mul, &Binop<JITAssembler>::inline_mul32, &Binop<JITAssembler>::inline_mul32), // OpMul
-
- OP(div), // OpDiv
- OP(mod), // OpMod
-
- INLINE_OP(shl, &Binop<JITAssembler>::inline_shl32, &Binop<JITAssembler>::inline_shl32), // OpLShift
- INLINE_OP(shr, &Binop<JITAssembler>::inline_shr32, &Binop<JITAssembler>::inline_shr32), // OpRShift
- INLINE_OP(ushr, &Binop<JITAssembler>::inline_ushr32, &Binop<JITAssembler>::inline_ushr32), // OpURShift
-
- OP(greaterThan), // OpGt
- OP(lessThan), // OpLt
- OP(greaterEqual), // OpGe
- OP(lessEqual), // OpLe
- OP(equal), // OpEqual
- OP(notEqual), // OpNotEqual
- OP(strictEqual), // OpStrictEqual
- OP(strictNotEqual), // OpStrictNotEqual
-
- OPCONTEXT(instanceof), // OpInstanceof
- OPCONTEXT(in), // OpIn
-
- NULL_OP, // OpAnd
- NULL_OP // OpOr
-};
-
-
-
-template <typename JITAssembler>
-void Binop<JITAssembler>::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
-{
- if (op != IR::OpMod
- && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) {
- doubleBinop(lhs, rhs, target);
- return;
- }
- if (lhs->type == IR::SInt32Type && rhs->type == IR::SInt32Type) {
- if (int32Binop(lhs, rhs, target))
- return;
- }
-
- Jump done;
- if (lhs->type != IR::StringType && rhs->type != IR::StringType)
- done = genInlineBinop(lhs, rhs, target);
-
- // TODO: inline var===null and var!==null
- Binop::OpInfo info = Binop::operation(op);
-
- if (op == IR::OpAdd &&
- (lhs->type == IR::StringType || rhs->type == IR::StringType)) {
- const Binop::OpInfo stringAdd = OPCONTEXT(addString);
- info = stringAdd;
- }
-
- typename JITAssembler::RuntimeCall fallBack(info.fallbackImplementation);
- typename JITAssembler::RuntimeCall context(info.contextImplementation);
- if (fallBack.isValid()) {
- as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack,
- PointerToValue(lhs),
- PointerToValue(rhs));
- } else if (context.isValid()) {
- as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context,
- JITAssembler::EngineRegister,
- PointerToValue(lhs),
- PointerToValue(rhs));
- } else {
- Q_ASSERT(!"unreachable");
- }
-
- if (done.isSet())
- done.link(as);
-
-}
-
-template <typename JITAssembler>
-void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
-{
- IR::Temp *targetTemp = target->asTemp();
- FPRegisterID targetReg;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
- targetReg = (FPRegisterID) targetTemp->index;
- else
- targetReg = JITAssembler::FPGpr0;
-
- switch (op) {
- case IR::OpAdd:
- if (lhs->asConst())
- std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant
-
- if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg))
- break;
-
- as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
- break;
-
- case IR::OpMul:
- if (lhs->asConst())
- std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant
-
- if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg))
- break;
-
- as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
- break;
-
- case IR::OpSub:
- if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg))
- break;
-
- if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp
- as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1);
- as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg);
- break;
- }
-
- as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
- break;
-
- case IR::OpDiv:
- if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg))
- break;
-
- if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp
- as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1);
- as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg);
- break;
- }
-
- as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg);
- break;
-
- default: {
- Jump trueCase = as->branchDouble(false, op, lhs, rhs);
- as->storeBool(false, target);
- Jump done = as->jump();
- trueCase.link(as);
- as->storeBool(true, target);
- done.link(as);
- } return;
- }
-
- if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeDouble(targetReg, target);
-}
-
-template <typename JITAssembler>
-bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- Q_ASSERT(leftSource->type == IR::SInt32Type);
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- switch (op) {
- case IR::OpBitAnd:
- case IR::OpBitOr:
- case IR::OpBitXor:
- case IR::OpAdd:
- case IR::OpMul:
- if (leftSource->asConst()) // X = Const op Y -> X = Y op Const
- std::swap(leftSource, rightSource);
- else if (IR::Temp *t = leftSource->asTemp()) {
- if (t->kind != IR::Temp::PhysicalRegister) // X = [address] op Y -> X = Y op [address]
- std::swap(leftSource, rightSource);
- }
- break;
-
- case IR::OpLShift:
- case IR::OpRShift:
- case IR::OpURShift:
- case IR::OpSub:
- // handled by this method, but we can't flip operands.
- break;
-
- default:
- return false; // not handled by this method, stop here.
- }
-
- bool inplaceOpWithAddress = false;
-
- IR::Temp *targetTemp = target->asTemp();
- RegisterID targetReg = JITAssembler::ReturnValueRegister;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- IR::Temp *rhs = rightSource->asTemp();
- if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) {
- // We try to load leftSource into the target's register, but we can't do that if
- // the target register is the same as rightSource.
- targetReg = (RegisterID) targetTemp->index;
- } else if (rhs && rhs->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->index) {
- // However, if the target register is the same as the rightSource register, we can flip
- // the operands for certain operations.
- switch (op) {
- case IR::OpBitAnd:
- case IR::OpBitOr:
- case IR::OpBitXor:
- case IR::OpAdd:
- case IR::OpMul:
- // X = Y op X -> X = X op Y (or rephrased: X op= Y (so an in-place operation))
- std::swap(leftSource, rightSource);
- targetReg = (RegisterID) targetTemp->index;
- break;
-
- case IR::OpLShift:
- case IR::OpRShift:
- case IR::OpURShift:
- case IR::OpSub:
- break;
-
- default:
- Q_UNREACHABLE();
- return false;
- }
- }
-
- // determine if we have X op= [address]
- if (IR::Temp *lhs = leftSource->asTemp()) {
- if (lhs->kind == IR::Temp::PhysicalRegister && lhs->index == targetTemp->index) {
- if (IR::Temp *rhs = rightSource->asTemp()) {
- if (rhs->kind != IR::Temp::PhysicalRegister) {
- switch (op) {
- case IR::OpBitAnd:
- case IR::OpBitOr:
- case IR::OpBitXor:
- case IR::OpAdd:
- case IR::OpMul:
- inplaceOpWithAddress = true;
- break;
- default:
- break;
- }
- }
- }
- }
- }
- }
-
- // Special cases:
- switch (op) {
- case IR::OpSub:
- if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rightSource->asTemp()->index) {
- // X = Y - X -> Tmp = X; X = Y; X -= Tmp
- targetReg = (RegisterID) targetTemp->index;
- as->move(targetReg, JITAssembler::ScratchRegister);
- as->move(as->toInt32Register(leftSource, targetReg), targetReg);
- as->sub32(JITAssembler::ScratchRegister, targetReg);
- } else {
- as->move(as->toInt32Register(leftSource, targetReg), targetReg);
- as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg);
- }
- as->storeInt32(targetReg, target);
- return true;
-
- case IR::OpLShift:
- case IR::OpRShift:
- case IR::OpURShift:
- if (IR::Const *c = rightSource->asConst()) {
- if ((QV4::Primitive::toUInt32(c->value) & 0x1f) == 0) {
- RegisterID r = as->toInt32Register(leftSource, targetReg);
- as->storeInt32(r, target);
- return true;
- }
- }
- break;
-
- default:
- break;
- }
-
- RegisterID l = as->toInt32Register(leftSource, targetReg);
- if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const
- TrustedImm32 r(int(c->value));
- TrustedImm32 ur(QV4::Primitive::toUInt32(c->value) & 0x1f);
-
- switch (op) {
- case IR::OpBitAnd: as->and32(r, l, targetReg); break;
- case IR::OpBitOr: as->or32 (r, l, targetReg); break;
- case IR::OpBitXor: as->xor32(r, l, targetReg); break;
- case IR::OpAdd: as->add32(r, l, targetReg); break;
- case IR::OpMul: as->mul32(r, l, targetReg); break;
-
- case IR::OpLShift: as->lshift32(l, ur, targetReg); break;
- case IR::OpRShift: as->rshift32(l, ur, targetReg); break;
- case IR::OpURShift: as->urshift32(l, ur, targetReg);
- as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
- return true;
-
- case IR::OpSub: // already handled before
- default: // not handled by this method:
- Q_UNREACHABLE();
- return false;
- }
- } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y]
- Pointer rhsAddr = as->loadAddressForReading(JITAssembler::ScratchRegister, rightSource);
- switch (op) {
- case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break;
- case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break;
- case IR::OpBitXor: as->xor32(rhsAddr, targetReg); break;
- case IR::OpAdd: as->add32(rhsAddr, targetReg); break;
- case IR::OpMul: as->mul32(rhsAddr, targetReg); break;
- break;
-
- default: // not handled by this method:
- Q_UNREACHABLE();
- return false;
- }
- } else { // All cases of Z = X op Y
- RegisterID r = as->toInt32Register(rightSource, JITAssembler::ScratchRegister);
- switch (op) {
- case IR::OpBitAnd: as->and32(l, r, targetReg); break;
- case IR::OpBitOr: as->or32 (l, r, targetReg); break;
- case IR::OpBitXor: as->xor32(l, r, targetReg); break;
- case IR::OpAdd: as->add32(l, r, targetReg); break;
- case IR::OpMul: as->mul32(l, r, targetReg); break;
-
-#if CPU(X86) || CPU(X86_64)
- // Intel does the & 0x1f on the CPU, so:
- case IR::OpLShift: as->lshift32(l, r, targetReg); break;
- case IR::OpRShift: as->rshift32(l, r, targetReg); break;
- case IR::OpURShift: as->urshift32(l, r, targetReg);
- as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
- return true;
-#else
- // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do
- // surprising stuff when shifting over 0 bits.
-#define CHECK_RHS(op) { \
- as->and32(TrustedImm32(0x1f), r, JITAssembler::ScratchRegister); \
- Jump notZero = as->branch32(RelationalCondition::NotEqual, JITAssembler::ScratchRegister, TrustedImm32(0)); \
- as->move(l, targetReg); \
- Jump done = as->jump(); \
- notZero.link(as); \
- op; \
- done.link(as); \
-}
- case IR::OpLShift: CHECK_RHS(as->lshift32(l, JITAssembler::ScratchRegister, targetReg)); break;
- case IR::OpRShift: CHECK_RHS(as->rshift32(l, JITAssembler::ScratchRegister, targetReg)); break;
- case IR::OpURShift:
- CHECK_RHS(as->urshift32(l, JITAssembler::ScratchRegister, targetReg));
- as->storeUInt32(targetReg, target);
- // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
- return true;
-#undef CHECK_RHS
-#endif
-
- case IR::OpSub: // already handled before
- default: // not handled by this method:
- Q_UNREACHABLE();
- return false;
- }
- }
-
- as->storeInt32(targetReg, target);
- return true;
-}
-
-template <typename JITAssembler>
-inline typename JITAssembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint)
-{
- if (IR::Temp *t = shouldNotOverlap->asTemp())
- if (t->type == IR::DoubleType)
- if (t->kind == IR::Temp::PhysicalRegister)
- if (t->index == hint)
- return typename JITAssembler::FPRegisterID(hint + 1);
- return typename JITAssembler::FPRegisterID(hint);
-}
-
-template <typename JITAssembler>
-typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- Jump done;
-
- // Try preventing a call for a few common binary operations. This is used in two cases:
- // - no register allocation was performed (not available for the platform, or the IR was
- // not transformed into SSA)
- // - type inference found that either or both operands can be of non-number type, and the
- // register allocator will have prepared for a call (meaning: all registers that do not
- // hold operands are spilled to the stack, which makes them available here)
- // Note: FPGPr0 can still not be used, because uint32->double conversion uses it as a scratch
- // register.
- switch (op) {
- case IR::OpAdd: {
- FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2);
- FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4);
- Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg);
- Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg);
-
- as->addDouble(rReg, lReg);
- as->storeDouble(lReg, target);
- done = as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(as);
- } break;
- case IR::OpMul: {
- FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2);
- FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4);
- Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg);
- Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg);
-
- as->mulDouble(rReg, lReg);
- as->storeDouble(lReg, target);
- done = as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(as);
- } break;
- case IR::OpSub: {
- FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2);
- FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4);
- Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg);
- Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg);
-
- as->subDouble(rReg, lReg);
- as->storeDouble(lReg, target);
- done = as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(as);
- } break;
- case IR::OpDiv: {
- FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2);
- FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4);
- Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg);
- Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg);
-
- as->divDouble(rReg, lReg);
- as->storeDouble(lReg, target);
- done = as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(as);
- } break;
- default:
- break;
- }
-
- return done;
-}
-
-template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>;
-#if defined(V4_BOOTSTRAP)
-#if !CPU(ARM_THUMB2)
-template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>;
-#endif
-#if !CPU(ARM64)
-template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>;
-#endif
-#endif
-
-} // end of namespace JIT
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-
-#endif
diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h
deleted file mode 100644
index 1b1ab7f24d..0000000000
--- a/src/qml/jit/qv4binop_p.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4BINOP_P_H
-#define QV4BINOP_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <qv4jsir_p.h>
-#include <qv4isel_masm_p.h>
-#include <qv4assembler_p.h>
-
-QT_BEGIN_NAMESPACE
-
-#if ENABLE(ASSEMBLER)
-
-namespace QV4 {
-namespace JIT {
-
-template <typename JITAssembler>
-struct Binop {
- Binop(JITAssembler *assembler, IR::AluOp operation)
- : as(assembler)
- , op(operation)
- {}
-
- using Jump = typename JITAssembler::Jump;
- using Address = typename JITAssembler::Address;
- using RegisterID = typename JITAssembler::RegisterID;
- using FPRegisterID = typename JITAssembler::FPRegisterID;
- using TrustedImm32 = typename JITAssembler::TrustedImm32;
- using ResultCondition = typename JITAssembler::ResultCondition;
- using RelationalCondition = typename JITAssembler::RelationalCondition;
- using Pointer = typename JITAssembler::Pointer;
- using PointerToValue = typename JITAssembler::PointerToValue;
-
- void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target);
- void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target);
- bool int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target);
- Jump genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target);
-
- typedef Jump (Binop::*MemRegOp)(Address, RegisterID);
- typedef Jump (Binop::*ImmRegOp)(TrustedImm32, RegisterID);
-
- struct OpInfo {
- const char *name;
- Runtime::RuntimeMethods fallbackImplementation;
- Runtime::RuntimeMethods contextImplementation;
- MemRegOp inlineMemRegOp;
- ImmRegOp inlineImmRegOp;
- bool needsExceptionCheck;
- };
-
- static const OpInfo operations[IR::LastAluOp + 1];
- static const OpInfo &operation(IR::AluOp operation)
- { return operations[operation]; }
-
- Jump inline_add32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- return as->branchAdd32(ResultCondition::Overflow, addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg);
-#endif
- }
-
- Jump inline_add32(TrustedImm32 imm, RegisterID reg)
- {
- return as->branchAdd32(ResultCondition::Overflow, imm, reg);
- }
-
- Jump inline_sub32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- return as->branchSub32(ResultCondition::Overflow, addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg);
-#endif
- }
-
- Jump inline_sub32(TrustedImm32 imm, RegisterID reg)
- {
- return as->branchSub32(ResultCondition::Overflow, imm, reg);
- }
-
- Jump inline_mul32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- return as->branchMul32(JITAssembler::Overflow, addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg);
-#endif
- }
-
- Jump inline_mul32(TrustedImm32 imm, RegisterID reg)
- {
- return as->branchMul32(ResultCondition::Overflow, imm, reg, reg);
- }
-
- Jump inline_shl32(Address addr, RegisterID reg)
- {
- as->load32(addr, JITAssembler::ScratchRegister);
- as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister);
- as->lshift32(JITAssembler::ScratchRegister, reg);
- return Jump();
- }
-
- Jump inline_shl32(TrustedImm32 imm, RegisterID reg)
- {
- imm.m_value &= 0x1f;
- as->lshift32(imm, reg);
- return Jump();
- }
-
- Jump inline_shr32(Address addr, RegisterID reg)
- {
- as->load32(addr, JITAssembler::ScratchRegister);
- as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister);
- as->rshift32(JITAssembler::ScratchRegister, reg);
- return Jump();
- }
-
- Jump inline_shr32(TrustedImm32 imm, RegisterID reg)
- {
- imm.m_value &= 0x1f;
- as->rshift32(imm, reg);
- return Jump();
- }
-
- Jump inline_ushr32(Address addr, RegisterID reg)
- {
- as->load32(addr, JITAssembler::ScratchRegister);
- as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister);
- as->urshift32(JITAssembler::ScratchRegister, reg);
- return as->branchTest32(ResultCondition::Signed, reg, reg);
- }
-
- Jump inline_ushr32(TrustedImm32 imm, RegisterID reg)
- {
- imm.m_value &= 0x1f;
- as->urshift32(imm, reg);
- return as->branchTest32(ResultCondition::Signed, reg, reg);
- }
-
- Jump inline_and32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- as->and32(addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- as->and32(JITAssembler::ScratchRegister, reg);
-#endif
- return Jump();
- }
-
- Jump inline_and32(TrustedImm32 imm, RegisterID reg)
- {
- as->and32(imm, reg);
- return Jump();
- }
-
- Jump inline_or32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- as->or32(addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- as->or32(JITAssembler::ScratchRegister, reg);
-#endif
- return Jump();
- }
-
- Jump inline_or32(TrustedImm32 imm, RegisterID reg)
- {
- as->or32(imm, reg);
- return Jump();
- }
-
- Jump inline_xor32(Address addr, RegisterID reg)
- {
-#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
- as->xor32(addr, reg);
-#else
- as->load32(addr, JITAssembler::ScratchRegister);
- as->xor32(JITAssembler::ScratchRegister, reg);
-#endif
- return Jump();
- }
-
- Jump inline_xor32(TrustedImm32 imm, RegisterID reg)
- {
- as->xor32(imm, reg);
- return Jump();
- }
-
-
-
- JITAssembler *as;
- IR::AluOp op;
-};
-
-}
-}
-
-#endif
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
deleted file mode 100644
index 7784eb364e..0000000000
--- a/src/qml/jit/qv4isel_masm.cpp
+++ /dev/null
@@ -1,1684 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4isel_masm_p.h"
-#include "qv4runtime_p.h"
-#include "qv4lookup_p.h"
-#include "qv4ssa_p.h"
-#include "qv4regalloc_p.h"
-#include "qv4assembler_p.h"
-#include "qv4unop_p.h"
-#include "qv4binop_p.h"
-
-#include <QtCore/QBuffer>
-#include <QtCore/QCoreApplication>
-
-#include <assembler/LinkBuffer.h>
-#include <WTFStubs.h>
-
-#include <iostream>
-
-#if ENABLE(ASSEMBLER)
-
-#if USE(UDIS86)
-# include <udis86.h>
-#endif
-
-using namespace QV4;
-using namespace QV4::JIT;
-
-
-template <typename JITAssembler>
-InstructionSelection<JITAssembler>::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
- , _block(0)
- , _as(0)
- , compilationUnit(new CompilationUnit)
- , qmlEngine(qmlEngine)
-{
- compilationUnit->codeRefs.resize(module->functions.size());
- module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode;
-}
-
-template <typename JITAssembler>
-InstructionSelection<JITAssembler>::~InstructionSelection()
-{
- delete _as;
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::run(int functionIndex)
-{
- IR::Function *function = irModule->functions[functionIndex];
- qSwap(_function, function);
-
- IR::Optimizer opt(_function);
- opt.run(qmlEngine);
-
- static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC");
- if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) {
- RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo());
- regalloc.run(_function, opt);
- calculateRegistersToSave(regalloc.usedRegisters());
- } else {
- if (opt.isInSSA())
- // No register allocator available for this platform, or env. var was set, so:
- opt.convertOutOfSSA();
- ConvertTemps().toStackSlots(_function);
- IR::Optimizer::showMeTheCode(_function, "After stack slot allocation");
- calculateRegistersToSave(JITTargetPlatform::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator.
- }
- BitVector removableJumps = opt.calculateOptionalJumps();
- qSwap(_removableJumps, removableJumps);
-
- JITAssembler* oldAssembler = _as;
- _as = new JITAssembler(jsGenerator, _function, executableAllocator);
- _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array
- regularRegistersToSave.size(),
- fpRegistersToSave.size());
- _as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave);
-
- if (JITTargetPlatform::RegisterArgumentCount > 0)
- _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister);
- else
- _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister);
-
- _as->initializeLocalVariables();
-
- int lastLine = 0;
- for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) {
- IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlock(i + 1) : 0;
- _block = _function->basicBlock(i);
- if (_block->isRemoved())
- continue;
- _as->registerBlock(_block, nextBlock);
-
- for (IR::Stmt *s : _block->statements()) {
- if (s->location.isValid()) {
- if (int(s->location.startLine) != lastLine) {
- _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ScratchRegister);
- Address lineAddr(JITTargetPlatform::ScratchRegister, JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lineNumber)));
- _as->store32(TrustedImm32(s->location.startLine), lineAddr);
- lastLine = s->location.startLine;
- }
- }
- visit(s);
- }
- }
-
- if (!_as->exceptionReturnLabel.isSet())
- visitRet(0);
-
- int dummySize;
- JSC::MacroAssemblerCodeRef codeRef =_as->link(&dummySize);
- compilationUnit->codeRefs[functionIndex] = codeRef;
-
- qSwap(_function, function);
- delete _as;
- _as = oldAssembler;
- qSwap(_removableJumps, removableJumps);
-}
-
-template <typename JITAssembler>
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep()
-{
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
- result.adopt(compilationUnit.take());
- return result;
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result)
-{
- prepareCallData(args, 0);
-
- if (useFastLookups && func->global) {
- uint index = registerGlobalGetterLookup(*func->id);
- generateRuntimeCall(_as, result, callGlobalLookup,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(index),
- baseAddressForCallData());
- } else {
- generateRuntimeCall(_as, result, callActivationProperty,
- JITTargetPlatform::EngineRegister,
- StringToIndex(*func->id),
- baseAddressForCallData());
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinTypeofQmlContextProperty(IR::Expr *base,
- IR::Member::MemberKind kind,
- int propertyIndex, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister,
- PointerToValue(base),
- TrustedImm32(propertyIndex));
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- generateRuntimeCall(_as, result, typeofContextObjectProperty,
- JITTargetPlatform::EngineRegister, PointerToValue(base),
- TrustedImm32(propertyIndex));
- } else {
- Q_UNREACHABLE();
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
- IR::Expr *result)
-{
- generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister,
- PointerToValue(base), StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- generateRuntimeCall(_as, result, typeofElement,
- JITTargetPlatform::EngineRegister,
- PointerToValue(base), PointerToValue(index));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result)
-{
- generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister,
- StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
-{
- generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister,
- PointerToValue(value));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
-{
- generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister,
- Reference(base), StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister,
- Reference(base), PointerToValue(index));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result)
-{
- generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister,
- StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result)
-{
- _as->storeValue(JITAssembler::TargetPrimitive::fromBoolean(false), result);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg)
-{
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister,
- PointerToValue(arg));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinReThrow()
-{
- _as->jumpToExceptionHandler();
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result)
-{
- generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister);
-
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName)
-{
- generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
-{
- Q_ASSERT(arg);
- Q_ASSERT(result);
-
- generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
-{
- Q_ASSERT(arg);
- Q_ASSERT(result);
-
- generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg)
-{
- Q_ASSERT(arg);
-
- generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinPopScope()
-{
- generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name)
-{
- generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister,
- TrustedImm32(deletable), StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args)
-{
- Q_ASSERT(result);
-
- int length = prepareVariableArguments(args);
- generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister,
- baseAddressForCallArguments(), TrustedImm32(length));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray)
-{
- Q_ASSERT(result);
-
- int argc = 0;
-
- const int classId = registerJSClass(keyValuePairCount, keyValuePairs);
-
- IR::ExprList *it = keyValuePairs;
- for (int i = 0; i < keyValuePairCount; ++i, it = it->next) {
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier);
-
- if (!isData) {
- it = it->next;
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier);
- }
- }
-
- it = arrayEntries;
- uint arrayValueCount = 0;
- while (it) {
- uint index = it->expr->asConst()->value;
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (!isData) {
- it = it->next; // getter
- it = it->next; // setter
- continue;
- }
-
- ++arrayValueCount;
-
- // Index
- _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier);
-
- // Value
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier);
- it = it->next;
- }
-
- it = arrayEntries;
- uint arrayGetterSetterCount = 0;
- while (it) {
- uint index = it->expr->asConst()->value;
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (isData) {
- it = it->next; // value
- continue;
- }
-
- ++arrayGetterSetterCount;
-
- // Index
- _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier);
-
- // Getter
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier);
- it = it->next;
-
- // Setter
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier);
- it = it->next;
- }
-
- generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister,
- baseAddressForCallArguments(), TrustedImm32(classId),
- TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30)));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result)
-{
- generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject()
-{
- generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
-{
- Q_ASSERT(value);
-
- prepareCallData(args, 0);
- if (value->asConst())
- generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister,
- PointerToValue(value),
- baseAddressForCallData());
- else
- generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister,
- Reference(value),
- baseAddressForCallData());
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp)
-{
- WriteBarrier::Type barrier;
- Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, temp, &barrier);
- _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ReturnValueRegister);
- _as->loadPtr(Address(JITTargetPlatform::ReturnValueRegister,JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData))), JITTargetPlatform::ReturnValueRegister);
- _as->copyValue(addr, Address(JITTargetPlatform::ReturnValueRegister, offsetof(CallData, thisObject)), barrier);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp)
-{
- generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp)
-{
- generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp)
-{
- generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::Expr *target)
-{
- if (IR::Temp *targetTemp = target->asTemp()) {
- if (targetTemp->kind == IR::Temp::PhysicalRegister) {
- if (targetTemp->type == IR::DoubleType) {
- Q_ASSERT(sourceConst->type == IR::DoubleType);
- _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index);
- } else if (targetTemp->type == IR::SInt32Type) {
- Q_ASSERT(sourceConst->type == IR::SInt32Type);
- _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index);
- } else if (targetTemp->type == IR::UInt32Type) {
- Q_ASSERT(sourceConst->type == IR::UInt32Type);
- _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index);
- } else if (targetTemp->type == IR::BoolType) {
- Q_ASSERT(sourceConst->type == IR::BoolType);
- _as->move(TrustedImm32(convertToValue<Primitive>(sourceConst).int_32()),
- (RegisterID) targetTemp->index);
- } else {
- Q_UNREACHABLE();
- }
- return;
- }
- }
-
- _as->storeValue(convertToValue<typename JITAssembler::TargetPrimitive>(sourceConst), target);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target)
-{
- Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str);
- _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister);
- WriteBarrier::Type barrier;
- Pointer destAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, target, &barrier);
- JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr, barrier);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
-{
- int id = registerRegExp(sourceRegexp);
- generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target)
-{
- if (useFastLookups && name->global) {
- uint index = registerGlobalGetterLookup(*name->id);
- generateLookupCall(target, index, offsetof(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void);
- return;
- }
- generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName)
-{
- // ### should use a lookup call here
- generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty,
- JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target)
-{
- int id = closure->value;
- generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
-{
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateLookupCall(target, index, offsetof(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void);
- } else {
- generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister,
- PointerToValue(base), StringToIndex(name));
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject)
- generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired));
- else if (kind == IR::Member::MemberOfQmlContextObject)
- generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired));
- else if (kind == IR::Member::MemberOfIdObjectsArray)
- generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index));
- else
- Q_ASSERT(false);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target)
-{
- if (attachedPropertiesId != 0)
- generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex));
- else if (isSingleton)
- generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex),
- TrustedImm32(captureRequired));
- else
- generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex),
- TrustedImm32(captureRequired));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase,
- const QString &targetName)
-{
- if (useFastLookups) {
- uint index = registerSetterLookup(targetName);
- generateLookupCall(JITAssembler::Void, index, offsetof(QV4::Lookup, setter),
- JITTargetPlatform::EngineRegister,
- PointerToValue(targetBase),
- PointerToValue(source));
- } else {
- generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister,
- PointerToValue(targetBase), StringToIndex(targetName),
- PointerToValue(source));
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject)
- generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase),
- TrustedImm32(propertyIndex), PointerToValue(source));
- else if (kind == IR::Member::MemberOfQmlContextObject)
- generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase),
- TrustedImm32(propertyIndex), PointerToValue(source));
- else
- Q_ASSERT(false);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex)
-{
- generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase),
- TrustedImm32(propertyIndex), PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
-{
- if (0 && useFastLookups) {
- uint lookup = registerIndexedGetterLookup();
- generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter),
- JITTargetPlatform::EngineRegister,
- PointerToValue(base),
- PointerToValue(index));
- return;
- }
-
- generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister,
- PointerToValue(base), PointerToValue(index));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex)
-{
- if (0 && useFastLookups) {
- uint lookup = registerIndexedSetterLookup();
- generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter),
- JITTargetPlatform::EngineRegister,
- PointerToValue(targetBase), PointerToValue(targetIndex),
- PointerToValue(source));
- return;
- }
- generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister,
- PointerToValue(targetBase), PointerToValue(targetIndex),
- PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *sourceTemp = source->asTemp();
- IR::Temp *targetTemp = target->asTemp();
-
- if (sourceTemp && targetTemp && *sourceTemp == *targetTemp)
- return;
- if (IR::ArgLocal *sal = source->asArgLocal())
- if (IR::ArgLocal *tal = target->asArgLocal())
- if (*sal == *tal)
- return;
-
- if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) {
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- if (sourceTemp->type == IR::DoubleType)
- _as->moveDouble((FPRegisterID) sourceTemp->index,
- (FPRegisterID) targetTemp->index);
- else
- _as->move((RegisterID) sourceTemp->index,
- (RegisterID) targetTemp->index);
- return;
- } else {
- switch (sourceTemp->type) {
- case IR::DoubleType:
- _as->storeDouble((FPRegisterID) sourceTemp->index, target);
- break;
- case IR::SInt32Type:
- _as->storeInt32((RegisterID) sourceTemp->index, target);
- break;
- case IR::UInt32Type:
- _as->storeUInt32((RegisterID) sourceTemp->index, target);
- break;
- case IR::BoolType:
- _as->storeBool((RegisterID) sourceTemp->index, target);
- break;
- default:
- Q_ASSERT(!"Unreachable");
- break;
- }
- return;
- }
- } else if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- switch (targetTemp->type) {
- case IR::DoubleType:
- Q_ASSERT(source->type == IR::DoubleType);
- _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index);
- return;
- case IR::BoolType:
- Q_ASSERT(source->type == IR::BoolType);
- _as->toInt32Register(source, (RegisterID) targetTemp->index);
- return;
- case IR::SInt32Type:
- Q_ASSERT(source->type == IR::SInt32Type);
- _as->toInt32Register(source, (RegisterID) targetTemp->index);
- return;
- case IR::UInt32Type:
- Q_ASSERT(source->type == IR::UInt32Type);
- _as->toUInt32Register(source, (RegisterID) targetTemp->index);
- return;
- default:
- Q_ASSERT(!"Unreachable");
- break;
- }
- }
-
- WriteBarrier::Type barrier;
- Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrier);
- // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy:
- _as->memcopyValue(addr, source, JITTargetPlatform::ScratchRegister, barrier);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *sourceTemp = source->asTemp();
- IR::Temp *targetTemp = target->asTemp();
-
- if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) {
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- Q_ASSERT(sourceTemp->type == targetTemp->type);
-
- if (sourceTemp->type == IR::DoubleType) {
- _as->moveDouble((FPRegisterID) targetTemp->index, JITTargetPlatform::FPGpr0);
- _as->moveDouble((FPRegisterID) sourceTemp->index,
- (FPRegisterID) targetTemp->index);
- _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) sourceTemp->index);
- } else {
- _as->swap((RegisterID) sourceTemp->index,
- (RegisterID) targetTemp->index);
- }
- return;
- }
- } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) {
- if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
- // Note: a swap for two stack-slots can involve different types.
- WriteBarrier::Type barrierForSource, barrierForTarget;
- Pointer sAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, source, &barrierForSource);
- Pointer tAddr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrierForTarget);
- _as->loadRawValue(sAddr, JITTargetPlatform::FPGpr0);
- _as->loadRawValue(tAddr, JITTargetPlatform::FPGpr1);
- _as->storeRawValue(JITTargetPlatform::FPGpr1, sAddr, barrierForSource);
- _as->storeRawValue(JITTargetPlatform::FPGpr0, tAddr, barrierForTarget);
- return;
- }
- }
-
- IR::Expr *memExpr = !sourceTemp || sourceTemp->kind == IR::Temp::StackSlot ? source : target;
- IR::Temp *regTemp = sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister ? sourceTemp
- : targetTemp;
- Q_ASSERT(memExpr);
- Q_ASSERT(regTemp);
-
- WriteBarrier::Type barrier;
- Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, memExpr, &barrier);
- if (regTemp->type == IR::DoubleType) {
- _as->loadDouble(addr, JITTargetPlatform::FPGpr0);
- _as->storeDouble((FPRegisterID) regTemp->index, addr, barrier);
- _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index);
- } else if (regTemp->type == IR::UInt32Type) {
- _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister);
- _as->storeUInt32((RegisterID) regTemp->index, addr, barrier);
- _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index);
- } else {
- _as->load32(addr, JITTargetPlatform::ScratchRegister);
- _as->store32((RegisterID) regTemp->index, addr);
- if (regTemp->type != memExpr->type) {
- addr.offset += 4;
- quint32 tag;
- switch (regTemp->type) {
- case IR::BoolType:
- tag = quint32(JITAssembler::ValueTypeInternal::Boolean);
- break;
- case IR::SInt32Type:
- tag = quint32(JITAssembler::ValueTypeInternal::Integer);
- break;
- default:
- tag = 31337; // bogus value
- Q_UNREACHABLE();
- }
- _as->store32(TrustedImm32(tag), addr);
- _as->emitWriteBarrier(addr, barrier);
- }
- _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index);
- }
-}
-
-#define setOp(op, opName, operation) \
- do { \
- op = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \
- needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \
- } while (0)
-#define setOpContext(op, opName, operation) \
- do { \
- opContext = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \
- needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \
- } while (0)
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
-{
- QV4::JIT::Unop<JITAssembler> unop(_as, oper);
- unop.generate(source, target);
-}
-
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- QV4::JIT::Binop<JITAssembler> binop(_as, oper);
- binop.generate(leftSource, rightSource, target);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result)
-{
- prepareCallData(args, base);
-
- if (kind == IR::Member::MemberOfQmlScopeObject)
- generateRuntimeCall(_as, result, callQmlScopeObjectProperty,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(propertyIndex),
- baseAddressForCallData());
- else if (kind == IR::Member::MemberOfQmlContextObject)
- generateRuntimeCall(_as, result, callQmlContextObjectProperty,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(propertyIndex),
- baseAddressForCallData());
- else
- Q_ASSERT(false);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result)
-{
- Q_ASSERT(base != 0);
-
- prepareCallData(args, base);
-
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateRuntimeCall(_as, result, callPropertyLookup,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(index),
- baseAddressForCallData());
- } else {
- generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister,
- StringToIndex(name),
- baseAddressForCallData());
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
- IR::Expr *result)
-{
- Q_ASSERT(base != 0);
-
- prepareCallData(args, base);
- generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister,
- PointerToValue(index),
- baseAddressForCallData());
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target)
-{
- switch (target->type) {
- case IR::DoubleType:
- convertTypeToDouble(source, target);
- break;
- case IR::BoolType:
- convertTypeToBool(source, target);
- break;
- case IR::SInt32Type:
- convertTypeToSInt32(source, target);
- break;
- case IR::UInt32Type:
- convertTypeToUInt32(source, target);
- break;
- default:
- convertTypeSlowPath(source, target);
- break;
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target)
-{
- Q_ASSERT(target->type != IR::BoolType);
-
- if (target->type & IR::NumberType)
- unop(IR::OpUPlus, source, target);
- else
- copyValue(source, target);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target)
-{
- switch (source->type) {
- case IR::SInt32Type:
- case IR::BoolType:
- case IR::NullType:
- convertIntToDouble(source, target);
- break;
- case IR::UInt32Type:
- convertUIntToDouble(source, target);
- break;
- case IR::UndefinedType:
- _as->loadDouble(_as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0);
- _as->storeDouble(JITTargetPlatform::FPGpr0, target);
- break;
- case IR::StringType:
- case IR::VarType: {
- // load the tag:
- Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source);
- tagAddr.offset += 4;
- _as->load32(tagAddr, JITTargetPlatform::ScratchRegister);
-
- // check if it's an int32:
- Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)));
- convertIntToDouble(source, target);
- Jump intDone = _as->jump();
-
- // not an int, check if it's NOT a double:
- isNoInt.link(_as);
- Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister);
-
- generateRuntimeCall(_as, target, toDouble, PointerToValue(source));
- Jump noDoubleDone = _as->jump();
-
- // it is a double:
- isDbl.link(_as);
- Pointer addr2 = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source);
- IR::Temp *targetTemp = target->asTemp();
- if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
- _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister);
- } else {
- _as->loadDouble(addr2, (FPRegisterID) targetTemp->index);
- }
-
- noDoubleDone.link(_as);
- intDone.link(_as);
- } break;
- default:
- convertTypeSlowPath(source, target);
- break;
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *sourceTemp = source->asTemp();
- switch (source->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- convertIntToBool(source, target);
- break;
- case IR::DoubleType: {
- // The source is in a register if the register allocator is used. If the register
- // allocator was not used, then that means that we can use any register for to
- // load the double into.
- FPRegisterID reg;
- if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister)
- reg = (FPRegisterID) sourceTemp->index;
- else
- reg = _as->toDoubleRegister(source, (FPRegisterID) 1);
- Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0);
-
- // it's 0, so false:
- _as->storeBool(false, target);
- Jump done = _as->jump();
-
- // it's non-zero, so true:
- nonZero.link(_as);
- _as->storeBool(true, target);
-
- // done:
- done.link(_as);
- } break;
- case IR::UndefinedType:
- case IR::NullType:
- _as->storeBool(false, target);
- break;
- case IR::StringType:
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean,
- PointerToValue(source));
- _as->storeBool(JITTargetPlatform::ReturnValueRegister, target);
- Q_FALLTHROUGH();
- case IR::VarType:
- default:
- Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source);
- Pointer tagAddr = addr;
- tagAddr.offset += 4;
- _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister);
-
- // checkif it's a bool:
- Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean)));
- _as->load32(addr, JITTargetPlatform::ReturnValueRegister);
- Jump boolDone = _as->jump();
- // check if it's an int32:
- notBool.link(_as);
- Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)));
- _as->load32(addr, JITTargetPlatform::ReturnValueRegister);
- Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister,
- TrustedImm32(0));
- _as->move(TrustedImm32(1), JITTargetPlatform::ReturnValueRegister);
- Jump intDone = _as->jump();
-
- // not an int:
- fallback.link(_as);
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean,
- PointerToValue(source));
-
- isZero.link(_as);
- intDone.link(_as);
- boolDone.link(_as);
- _as->storeBool(JITTargetPlatform::ReturnValueRegister, target);
-
- break;
- }
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target)
-{
- switch (source->type) {
- case IR::VarType: {
- JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target);
- } break;
- case IR::DoubleType: {
- Jump success =
- _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source),
- JITTargetPlatform::ReturnValueRegister,
- BranchTruncateType::BranchIfTruncateSuccessful);
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt,
- PointerToValue(source));
- success.link(_as);
- _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target);
- } break;
- case IR::UInt32Type:
- _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target);
- break;
- case IR::NullType:
- case IR::UndefinedType:
- _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister);
- _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target);
- break;
- case IR::BoolType:
- _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target);
- break;
- case IR::StringType:
- default:
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt,
- _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source));
- _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target);
- break;
- } // switch (source->type)
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target)
-{
- switch (source->type) {
- case IR::VarType: {
- // load the tag:
- Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source);
- tagAddr.offset += 4;
- _as->load32(tagAddr, JITTargetPlatform::ScratchRegister);
-
- // check if it's an int32:
- Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)));
- Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source);
- _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target);
- Jump intDone = _as->jump();
-
- // not an int:
- isNoInt.link(_as);
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt,
- _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source));
- _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target);
-
- intDone.link(_as);
- } break;
- case IR::DoubleType: {
- FPRegisterID reg = _as->toDoubleRegister(source);
- Jump success =
- _as->branchTruncateDoubleToUint32(reg, JITTargetPlatform::ReturnValueRegister,
- BranchTruncateType::BranchIfTruncateSuccessful);
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToUInt,
- PointerToValue(source));
- success.link(_as);
- _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target);
- } break;
- case IR::NullType:
- case IR::UndefinedType:
- _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister);
- _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target);
- break;
- case IR::StringType:
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt,
- PointerToValue(source));
- _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target);
- break;
- case IR::SInt32Type:
- case IR::BoolType:
- _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target);
- break;
- default:
- break;
- } // switch (source->type)
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result)
-{
- Q_ASSERT(func != 0);
- prepareCallData(args, 0);
-
- if (useFastLookups && func->global) {
- uint index = registerGlobalGetterLookup(*func->id);
- generateRuntimeCall(_as, result, constructGlobalLookup,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(index), baseAddressForCallData());
- return;
- }
-
- generateRuntimeCall(_as, result, constructActivationProperty,
- JITTargetPlatform::EngineRegister,
- StringToIndex(*func->id),
- baseAddressForCallData());
-}
-
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result)
-{
- prepareCallData(args, base);
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateRuntimeCall(_as, result, constructPropertyLookup,
- JITTargetPlatform::EngineRegister,
- TrustedImm32(index),
- baseAddressForCallData());
- return;
- }
-
- generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister,
- StringToIndex(name),
- baseAddressForCallData());
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
-{
- Q_ASSERT(value != 0);
-
- prepareCallData(args, 0);
- generateRuntimeCall(_as, result, constructValue,
- JITTargetPlatform::EngineRegister,
- Reference(value),
- baseAddressForCallData());
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s)
-{
- if (!_removableJumps.at(_block->index()))
- _as->jumpToBlock(_block, s->target);
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s)
-{
- IR::Temp *t = s->cond->asTemp();
- if (t || s->cond->asArgLocal()) {
- RegisterID reg;
- if (t && t->kind == IR::Temp::PhysicalRegister) {
- Q_ASSERT(t->type == IR::BoolType);
- reg = (RegisterID) t->index;
- } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) {
- reg = JITTargetPlatform::ReturnValueRegister;
- _as->toInt32Register(t, reg);
- } else {
- Address temp = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, s->cond);
- Address tag = temp;
- tag.offset += QV4::Value::tagOffset();
- Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean)));
-
- Address data = temp;
- data.offset += QV4::Value::valueOffset();
- _as->load32(data, JITTargetPlatform::ReturnValueRegister);
- Jump testBoolean = _as->jump();
-
- booleanConversion.link(_as);
- reg = JITTargetPlatform::ReturnValueRegister;
- generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond));
-
- testBoolean.link(_as);
- }
-
- _as->generateCJumpOnNonZero(reg, _block, s->iftrue, s->iffalse);
- return;
- } else if (IR::Const *c = s->cond->asConst()) {
- // TODO: SSA optimization for constant condition evaluation should remove this.
- // See also visitCJump() in RegAllocInfo.
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean,
- PointerToValue(c));
- _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse);
- return;
- } else if (IR::Binop *b = s->cond->asBinop()) {
- if (b->left->type == IR::DoubleType && b->right->type == IR::DoubleType
- && visitCJumpDouble(b->op, b->left, b->right, s->iftrue, s->iffalse))
- return;
-
- if (b->left->type == IR::SInt32Type && b->right->type == IR::SInt32Type
- && visitCJumpSInt32(b->op, b->left, b->right, s->iftrue, s->iffalse))
- return;
-
- if (b->op == IR::OpStrictEqual || b->op == IR::OpStrictNotEqual) {
- visitCJumpStrict(b, s->iftrue, s->iffalse);
- return;
- }
- if (b->op == IR::OpEqual || b->op == IR::OpNotEqual) {
- visitCJumpEqual(b, s->iftrue, s->iffalse);
- return;
- }
-
- typename JITAssembler::RuntimeCall op;
- typename JITAssembler::RuntimeCall opContext;
- const char *opName = 0;
- bool needsExceptionCheck;
- switch (b->op) {
- default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break;
- case IR::OpGt: setOp(op, opName, compareGreaterThan); break;
- case IR::OpLt: setOp(op, opName, compareLessThan); break;
- case IR::OpGe: setOp(op, opName, compareGreaterEqual); break;
- case IR::OpLe: setOp(op, opName, compareLessEqual); break;
- case IR::OpEqual: setOp(op, opName, compareEqual); break;
- case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break;
- case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break;
- case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break;
- case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break;
- case IR::OpIn: setOpContext(op, opName, compareIn); break;
- } // switch
-
- // TODO: in SSA optimization, do constant expression evaluation.
- // The case here is, for example:
- // if (true === true) .....
- // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block)
- // elimination (which isn't there either) would remove the whole else block.
- if (opContext.isValid())
- _as->generateFunctionCallImp(needsExceptionCheck,
- JITTargetPlatform::ReturnValueRegister, opName, opContext,
- JITTargetPlatform::EngineRegister,
- PointerToValue(b->left),
- PointerToValue(b->right));
- else
- _as->generateFunctionCallImp(needsExceptionCheck,
- JITTargetPlatform::ReturnValueRegister, opName, op,
- PointerToValue(b->left),
- PointerToValue(b->right));
-
- _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse);
- return;
- }
- Q_UNREACHABLE();
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s)
-{
- _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave);
-}
-
-template <typename JITAssembler>
-int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args)
-{
- int argc = 0;
- for (IR::ExprList *it = args; it; it = it->next) {
- ++argc;
- }
-
- int i = 0;
- for (IR::ExprList *it = args; it; it = it->next, ++i) {
- IR::Expr *arg = it->expr;
- Q_ASSERT(arg != 0);
- Pointer dst(_as->stackLayout().argumentAddressForCall(i));
- if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister)
- _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier);
- else
- _as->copyValue(dst, arg, WriteBarrier::NoBarrier);
- }
-
- return argc;
-}
-
-template <typename JITAssembler>
-int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR::Expr *thisObject)
-{
- int argc = 0;
- for (IR::ExprList *it = args; it; it = it->next) {
- ++argc;
- }
-
- Pointer p = _as->stackLayout().callDataAddress(offsetof(CallData, tag));
- _as->store32(TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)), p);
- p = _as->stackLayout().callDataAddress(offsetof(CallData, argc));
- _as->store32(TrustedImm32(argc), p);
- p = _as->stackLayout().callDataAddress(offsetof(CallData, thisObject));
- if (!thisObject)
- _as->storeValue(JITAssembler::TargetPrimitive::undefinedValue(), p, WriteBarrier::NoBarrier);
- else
- _as->copyValue(p, thisObject, WriteBarrier::NoBarrier);
-
- int i = 0;
- for (IR::ExprList *it = args; it; it = it->next, ++i) {
- IR::Expr *arg = it->expr;
- Q_ASSERT(arg != 0);
- Pointer dst(_as->stackLayout().argumentAddressForCall(i));
- if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister)
- _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier);
- else
- _as->copyValue(dst, arg, WriteBarrier::NoBarrier);
- }
- return argc;
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used)
-{
- regularRegistersToSave.clear();
- fpRegistersToSave.clear();
-
- for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) {
- if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) {
- regularRegistersToSave.append(ri);
- continue;
- }
- if (ri.isCallerSaved())
- continue;
- if (ri.isRegularRegister()) {
- if (ri.isPredefined() || used.contains(ri))
- regularRegistersToSave.append(ri);
- } else {
- Q_ASSERT(ri.isFloatingPoint());
- if (ri.isPredefined() || used.contains(ri))
- fpRegistersToSave.append(ri);
- }
- }
-}
-
-QT_BEGIN_NAMESPACE
-namespace QV4 {
-bool operator==(const Primitive &v1, const Primitive &v2)
-{
- return v1.rawValue() == v2.rawValue();
-}
-} // QV4 namespace
-QT_END_NAMESPACE
-
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right,
- IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
-{
- if (_as->nextBlock() == iftrue) {
- Jump target = _as->branchDouble(true, op, left, right);
- _as->addPatch(iffalse, target);
- } else {
- Jump target = _as->branchDouble(false, op, left, right);
- _as->addPatch(iftrue, target);
- _as->jumpToBlock(_block, iffalse);
- }
- return true;
-}
-
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right,
- IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
-{
- if (_as->nextBlock() == iftrue) {
- Jump target = _as->branchInt32(true, op, left, right);
- _as->addPatch(iffalse, target);
- } else {
- Jump target = _as->branchInt32(false, op, left, right);
- _as->addPatch(iftrue, target);
- _as->jumpToBlock(_block, iffalse);
- }
- return true;
-}
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual);
-
- if (visitCJumpStrictNull(binop, trueBlock, falseBlock))
- return;
- if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock))
- return;
- if (visitCJumpStrictBool(binop, trueBlock, falseBlock))
- return;
-
- IR::Expr *left = binop->left;
- IR::Expr *right = binop->right;
-
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareStrictEqual,
- PointerToValue(left), PointerToValue(right));
- _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal,
- JITTargetPlatform::ReturnValueRegister, TrustedImm32(0),
- _block, trueBlock, falseBlock);
-}
-
-// Only load the non-null temp.
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- IR::Expr *varSrc = 0;
- if (binop->left->type == IR::VarType && binop->right->type == IR::NullType)
- varSrc = binop->left;
- else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType)
- varSrc = binop->right;
- if (!varSrc)
- return false;
-
- if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- if (IR::Const *c = varSrc->asConst()) {
- if (c->type == IR::NullType)
- _as->jumpToBlock(_block, trueBlock);
- else
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc);
- tagAddr.offset += 4;
- const RegisterID tagReg = JITTargetPlatform::ScratchRegister;
- _as->load32(tagAddr, tagReg);
-
- RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal
- : RelationalCondition::NotEqual;
- const TrustedImm32 tag{quint32(JITAssembler::ValueTypeInternal::Null)};
- _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
- return true;
-}
-
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- IR::Expr *varSrc = 0;
- if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType)
- varSrc = binop->left;
- else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType)
- varSrc = binop->right;
- if (!varSrc)
- return false;
-
- if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- if (IR::Const *c = varSrc->asConst()) {
- if (c->type == IR::UndefinedType)
- _as->jumpToBlock(_block, trueBlock);
- else
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal
- : RelationalCondition::NotEqual;
- const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister;
- _as->generateCJumpOnUndefined(cond, varSrc, JITTargetPlatform::ScratchRegister, tagReg, _block, trueBlock, falseBlock);
- return true;
-}
-
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- IR::Expr *boolSrc = 0, *otherSrc = 0;
- if (binop->left->type == IR::BoolType) {
- boolSrc = binop->left;
- otherSrc = binop->right;
- } else if (binop->right->type == IR::BoolType) {
- boolSrc = binop->right;
- otherSrc = binop->left;
- } else {
- // neither operands are statically typed as bool, so bail out.
- return false;
- }
- if (otherSrc->type == IR::UnknownType) {
- // Ok, we really need to call into the runtime.
- // (This case doesn't happen when the optimizer ran, because everything will be typed (yes,
- // possibly as "var" meaning anything), but it does happen for $0===true, which is generated
- // for things where the optimizer didn't run (like functions with a try block).)
- return false;
- }
-
- RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal
- : RelationalCondition::NotEqual;
-
- if (otherSrc->type == IR::BoolType) { // both are boolean
- RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister);
- RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister);
- _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock);
- return true;
- }
-
- if (otherSrc->type != IR::VarType) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- Pointer otherAddr = _as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, otherSrc);
- otherAddr.offset += 4; // tag address
-
- // check if the tag of the var operand is indicates 'boolean'
- _as->load32(otherAddr, JITTargetPlatform::ScratchRegister);
- Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister,
- TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean)));
- if (binop->op == IR::OpStrictEqual)
- _as->addPatch(falseBlock, noBool);
- else
- _as->addPatch(trueBlock, noBool);
-
- // ok, both are boolean, so let's load them and compare them.
- otherAddr.offset -= 4; // int_32 address
- _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister);
- RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister);
- _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock,
- falseBlock);
- return true;
-}
-
-template <typename JITAssembler>
-bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType);
-
- IR::Expr *varSrc = 0;
- if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef)
- varSrc = binop->left;
- else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType)
- varSrc = binop->right;
- if (!varSrc)
- return false;
-
- if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- if (IR::Const *c = varSrc->asConst()) {
- if (c->type == nullOrUndef)
- _as->jumpToBlock(_block, trueBlock);
- else
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc);
- tagAddr.offset += 4;
- const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister;
- _as->load32(tagAddr, tagReg);
-
- if (binop->op == IR::OpNotEqual)
- qSwap(trueBlock, falseBlock);
- Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Null)));
- Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal)));
- tagAddr.offset -= 4;
- _as->load32(tagAddr, tagReg);
- Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0));
- _as->addPatch(trueBlock, isNull);
- _as->addPatch(falseBlock, isNotUndefinedTag);
- _as->addPatch(falseBlock, isNotUndefinedValue);
- _as->jumpToBlock(_block, trueBlock);
-
- return true;
-}
-
-
-template <typename JITAssembler>
-void InstructionSelection<JITAssembler>::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(binop->op == IR::OpEqual || binop->op == IR::OpNotEqual);
-
- if (visitCJumpNullUndefined(IR::NullType, binop, trueBlock, falseBlock))
- return;
-
- IR::Expr *left = binop->left;
- IR::Expr *right = binop->right;
-
- generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareEqual,
- PointerToValue(left), PointerToValue(right));
- _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal,
- JITTargetPlatform::ReturnValueRegister, TrustedImm32(0),
- _block, trueBlock, falseBlock);
-}
-
-template <typename JITAssembler>
-QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading()
-{
- QQmlRefPointer<CompiledData::CompilationUnit> result;
- result.adopt(new JIT::CompilationUnit);
- return result;
-}
-
-#endif // ENABLE(ASSEMBLER)
-
-QT_BEGIN_NAMESPACE
-namespace QV4 { namespace JIT {
-#if ENABLE(ASSEMBLER)
-template class Q_QML_EXPORT InstructionSelection<>;
-template class Q_QML_EXPORT ISelFactory<>;
-#endif
-
-#if defined(V4_BOOTSTRAP)
-
-Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture)
-{
-#if ENABLE(ASSEMBLER)
- using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
- using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
-
- if (architecture == QLatin1String("arm"))
- return new ISelFactory<ARMv7CrossAssembler>;
- else if (architecture == QLatin1String("arm64"))
- return new ISelFactory<ARM64CrossAssembler>;
-
- QString hostArch;
-#if CPU(ARM_THUMB2)
- hostArch = QStringLiteral("arm");
-#elif CPU(MIPS)
- hostArch = QStringLiteral("mips");
-#elif CPU(X86)
- hostArch = QStringLiteral("i386");
-#elif CPU(X86_64)
- hostArch = QStringLiteral("x86_64");
-#endif
- if (!hostArch.isEmpty() && architecture == hostArch)
- return new ISelFactory<>;
-#endif // ENABLE(ASSEMBLER)
-
- return nullptr;
-}
-
-#endif
-} }
-QT_END_NAMESPACE
-
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
deleted file mode 100644
index 869f857c41..0000000000
--- a/src/qml/jit/qv4isel_masm_p.h
+++ /dev/null
@@ -1,319 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4ISEL_MASM_P_H
-#define QV4ISEL_MASM_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include "private/qv4jsir_p.h"
-#include "private/qv4isel_p.h"
-#include "private/qv4isel_util_p.h"
-#include "private/qv4util_p.h"
-#include "private/qv4value_p.h"
-#include "private/qv4lookup_p.h"
-
-#include <QtCore/QHash>
-#include <QtCore/QStack>
-#include <config.h>
-#include <wtf/Vector.h>
-
-#include "qv4assembler_p.h"
-
-#if ENABLE(ASSEMBLER)
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace JIT {
-
-template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>>
-class Q_QML_EXPORT InstructionSelection:
- protected IR::IRDecoder,
- public EvalInstructionSelection
-{
-public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- ~InstructionSelection();
-
- void run(int functionIndex) override;
-
-protected:
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() override;
-
- void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override;
- void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinTypeofName(const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override;
- void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinDeleteName(const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteValue(IR::Expr *result) override;
- void callBuiltinThrow(IR::Expr *arg) override;
- void callBuiltinReThrow() override;
- void callBuiltinUnwindException(IR::Expr *) override;
- void callBuiltinPushCatchScope(const QString &exceptionName) override;
- void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinPushWithScope(IR::Expr *arg) override;
- void callBuiltinPopScope() override;
- void callBuiltinDeclareVar(bool deletable, const QString &name) override;
- void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override;
- void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override;
- void callBuiltinSetupArgumentObject(IR::Expr *result) override;
- void callBuiltinConvertThisToObject() override;
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override;
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override;
- void convertType(IR::Expr *source, IR::Expr *target) override;
- void loadThisObject(IR::Expr *temp) override;
- void loadQmlContext(IR::Expr *target) override;
- void loadQmlImportedScripts(IR::Expr *target) override;
- void loadQmlSingleton(const QString &name, IR::Expr *target) override;
- void loadConst(IR::Const *sourceConst, IR::Expr *target) override;
- void loadString(const QString &str, IR::Expr *target) override;
- void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override;
- void getActivationProperty(const IR::Name *name, IR::Expr *target) override;
- void setActivationProperty(IR::Expr *source, const QString &targetName) override;
- void initClosure(IR::Closure *closure, IR::Expr *target) override;
- void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override;
- void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override;
- void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override;
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override;
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override;
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override;
- void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override;
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override;
- void copyValue(IR::Expr *source, IR::Expr *target) override;
- void swapValues(IR::Expr *source, IR::Expr *target) override;
- void unop(IR::AluOp oper, IR::Expr *sourceTemp, IR::Expr *target) override;
- void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override;
-
- using Address = typename JITAssembler::Address;
- using Pointer = typename JITAssembler::Pointer;
- using PointerToValue = typename JITAssembler::PointerToValue;
- using RegisterID = typename JITAssembler::RegisterID;
- using FPRegisterID = typename JITAssembler::FPRegisterID;
- using ResultCondition = typename JITAssembler::ResultCondition;
- using TrustedImm32 = typename JITAssembler::TrustedImm32;
- using TrustedImm64 = typename JITAssembler::TrustedImm64;
- using Label = typename JITAssembler::Label;
- using Jump = typename JITAssembler::Jump;
- using StringToIndex = typename JITAssembler::StringToIndex;
- using Reference = typename JITAssembler::Reference;
- using RelationalCondition = typename JITAssembler::RelationalCondition;
- using BranchTruncateType = typename JITAssembler::BranchTruncateType;
- using RuntimeCall = typename JITAssembler::RuntimeCall;
-
- using JITTargetPlatform = typename JITAssembler::JITTargetPlatform;
-
- Address addressForArgument(int index) const
- {
- // FramePointerRegister points to its old value on the stack, and above
- // it we have the return address, hence the need to step over two
- // values before reaching the first argument.
- return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * JITTargetPlatform::RegisterSize);
- }
-
- Pointer baseAddressForCallArguments()
- {
- return _as->stackLayout().argumentAddressForCall(0);
- }
-
- Pointer baseAddressForCallData()
- {
- return _as->stackLayout().callDataAddress();
- }
-
- void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr*result) override;
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
-
- void visitJump(IR::Jump *) override;
- void visitCJump(IR::CJump *) override;
- void visitRet(IR::Ret *) override;
-
- bool visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right,
- IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
- bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right,
- IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
- void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- void visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
-
-private:
- void convertTypeSlowPath(IR::Expr *source, IR::Expr *target);
- void convertTypeToDouble(IR::Expr *source, IR::Expr *target);
- void convertTypeToBool(IR::Expr *source, IR::Expr *target);
- void convertTypeToSInt32(IR::Expr *source, IR::Expr *target);
- void convertTypeToUInt32(IR::Expr *source, IR::Expr *target);
-
- void convertIntToDouble(IR::Expr *source, IR::Expr *target)
- {
- if (IR::Temp *targetTemp = target->asTemp()) {
- if (targetTemp->kind == IR::Temp::PhysicalRegister) {
- if (IR::Temp *sourceTemp = source->asTemp()) {
- if (sourceTemp->kind == IR::Temp::PhysicalRegister) {
- _as->convertInt32ToDouble((RegisterID) sourceTemp->index,
- (FPRegisterID) targetTemp->index);
- } else {
- _as->convertInt32ToDouble(_as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, sourceTemp),
- (FPRegisterID) targetTemp->index);
- }
- } else {
- _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister),
- (FPRegisterID) targetTemp->index);
- }
-
- return;
- }
- }
-
- _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister),
- JITTargetPlatform::FPGpr0);
- _as->storeDouble(JITTargetPlatform::FPGpr0, target);
- }
-
- void convertUIntToDouble(IR::Expr *source, IR::Expr *target)
- {
- RegisterID tmpReg = JITTargetPlatform::ScratchRegister;
- RegisterID reg = _as->toInt32Register(source, tmpReg);
-
- if (IR::Temp *targetTemp = target->asTemp()) {
- if (targetTemp->kind == IR::Temp::PhysicalRegister) {
- _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg);
- return;
- }
- }
-
- _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg),
- JITTargetPlatform::FPGpr0, tmpReg);
- _as->storeDouble(JITTargetPlatform::FPGpr0, target);
- }
-
- void convertIntToBool(IR::Expr *source, IR::Expr *target)
- {
- RegisterID reg = JITTargetPlatform::ScratchRegister;
-
- if (IR::Temp *targetTemp = target->asTemp())
- if (targetTemp->kind == IR::Temp::PhysicalRegister)
- reg = (RegisterID) targetTemp->index;
- _as->move(_as->toInt32Register(source, reg), reg);
- _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg);
- _as->storeBool(reg, target);
- }
-
- int prepareVariableArguments(IR::ExprList* args);
- int prepareCallData(IR::ExprList* args, IR::Expr *thisObject);
-
- void calculateRegistersToSave(const RegisterInformation &used);
-
- template <typename Retval, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- // Note: using the return value register is intentional: for ABIs where the first parameter
- // goes into the same register as the return value (currently only ARM), the prepareCall
- // will combine loading the looupAddr into the register and calculating the indirect call
- // address.
- Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup));
-
- _as->generateFunctionCallImp(true, retval, "lookup getter/setter",
- typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr,
- arg1, arg2, arg3, arg4);
- }
-
- template <typename Retval, typename Arg1, typename Arg2>
- void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
- {
- generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType());
- }
-
- template <typename Retval, typename Arg1, typename Arg2, typename Arg3>
- void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, arg3, typename JITAssembler::VoidType());
- }
-
- IR::BasicBlock *_block;
- BitVector _removableJumps;
- JITAssembler* _as;
-
- QScopedPointer<CompilationUnit> compilationUnit;
- QQmlEnginePrivate *qmlEngine;
- RegisterInformation regularRegistersToSave;
- RegisterInformation fpRegistersToSave;
-};
-
-template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>>
-class Q_QML_EXPORT ISelFactory: public EvalISelFactory
-{
-public:
- ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {}
- virtual ~ISelFactory() {}
- EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) override final
- { return new InstructionSelection<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); }
- bool jitCompileRegexps() const override final
- { return true; }
- QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() override final;
-};
-
-} // end of namespace JIT
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // ENABLE(ASSEMBLER)
-
-#endif // QV4ISEL_MASM_P_H
diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp
new file mode 100644
index 0000000000..a8bdd20fbd
--- /dev/null
+++ b/src/qml/jit/qv4jit.cpp
@@ -0,0 +1,1365 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4jit_p.h"
+#include "qv4assembler_p.h"
+#include <private/qv4lookup_p.h>
+
+#ifdef V4_ENABLE_JIT
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::JIT;
+using namespace QV4::Moth;
+
+ByteCodeHandler::~ByteCodeHandler()
+{
+}
+
+#define DISPATCH_INSTRUCTION(name, nargs, ...) \
+ generate_##name( \
+ __VA_ARGS__ \
+ );
+
+#define DECODE_AND_DISPATCH(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ Q_UNUSED(base_ptr); \
+ startInstruction(Instr::Type::instr); \
+ _offset = code - start; \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ continue; \
+ }
+
+void ByteCodeHandler::decode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+
+ FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
+ }
+}
+
+#undef DECODE_AND_DISPATCH
+#undef DISPATCH_INSTRUCTION
+
+BaselineJIT::BaselineJIT(Function *function)
+ : function(function)
+ , as(new Assembler(function->compilationUnit->constants))
+{}
+
+BaselineJIT::~BaselineJIT()
+{}
+
+void BaselineJIT::generate()
+{
+// qDebug()<<"jitting" << function->name()->toQString();
+ collectLabelsInBytecode();
+
+ as->generatePrologue();
+ decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize);
+ as->generateEpilogue();
+
+ as->link(function);
+// qDebug()<<"done";
+}
+
+#define STORE_IP() as->storeInstructionPointer(instructionOffset())
+#define STORE_ACC() as->saveAccumulatorInFrame()
+
+void BaselineJIT::generate_Ret()
+{
+ as->ret();
+}
+
+void BaselineJIT::generate_Debug() { Q_UNREACHABLE(); }
+
+void BaselineJIT::generate_LoadConst(int index)
+{
+ as->loadConst(index);
+}
+
+void BaselineJIT::generate_LoadZero()
+{
+ as->loadValue(Encode(int(0)));
+}
+
+void BaselineJIT::generate_LoadTrue()
+{
+ as->loadValue(Encode(true));
+}
+
+void BaselineJIT::generate_LoadFalse()
+{
+ as->loadValue(Encode(false));
+}
+
+void BaselineJIT::generate_LoadNull()
+{
+ as->loadValue(Encode::null());
+}
+
+void BaselineJIT::generate_LoadUndefined()
+{
+ as->loadValue(Encode::undefined());
+}
+
+void BaselineJIT::generate_LoadInt(int value)
+{
+ //###
+ as->loadValue(Encode(value));
+}
+
+void BaselineJIT::generate_MoveConst(int constIndex, int destTemp)
+{
+ as->copyConst(constIndex, destTemp);
+}
+
+void BaselineJIT::generate_LoadReg(int reg)
+{
+ as->loadReg(reg);
+}
+
+void BaselineJIT::generate_StoreReg(int reg)
+{
+ as->storeReg(reg);
+}
+
+void BaselineJIT::generate_MoveReg(int srcReg, int destReg)
+{
+ as->loadReg(srcReg);
+ as->storeReg(destReg);
+}
+
+static ReturnedValue loadLocalHelper(const Value &context, int index)
+{
+ auto cc = static_cast<Heap::CallContext *>(context.m());
+ return cc->locals[uint(index)].asReturnedValue();
+}
+
+void BaselineJIT::generate_LoadLocal(int index)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passRegAsArg(CallData::Context, 0);
+ JIT_GENERATE_RUNTIME_CALL(loadLocalHelper, Assembler::ResultInAccumulator);
+}
+
+static void storeLocalHelper(ExecutionEngine *engine, const Value &context, int index, const Value &acc)
+{
+ auto cc = static_cast<Heap::CallContext *>(context.m());
+ QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc);
+}
+
+void BaselineJIT::generate_StoreLocal(int index)
+{
+ as->checkException();
+ as->prepareCallWithArgCount(4);
+ STORE_ACC();
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(index, 2);
+ as->passRegAsArg(CallData::Context, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(storeLocalHelper, Assembler::IgnoreResult);
+}
+
+static inline Heap::CallContext *getScope(Value *stack, int level)
+{
+ Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
+ while (level > 0) {
+ --level;
+ scope = scope->outer;
+ }
+ Q_ASSERT(scope);
+ return static_cast<Heap::CallContext *>(scope);
+}
+
+static ReturnedValue loadScopedLocalHelper(Value *stack, int scope, int index)
+{
+ auto cc = getScope(stack, scope);
+ return cc->locals[uint(index)].asReturnedValue();
+}
+
+void BaselineJIT::generate_LoadScopedLocal(int scope, int index)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passInt32AsArg(scope, 1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(loadScopedLocalHelper, Assembler::ResultInAccumulator);
+}
+
+static void storeScopedLocalHelper(ExecutionEngine *engine, Value *stack, int scope, int index,
+ const Value &acc)
+{
+ auto cc = getScope(stack, scope);
+ QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc);
+}
+
+void BaselineJIT::generate_StoreScopedLocal(int scope, int index)
+{
+ as->checkException();
+ as->prepareCallWithArgCount(5);
+ STORE_ACC();
+ as->passAccumulatorAsArg(4);
+ as->passInt32AsArg(index, 3);
+ as->passInt32AsArg(scope, 2);
+ as->passRegAsArg(0, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(storeScopedLocalHelper, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_LoadRuntimeString(int stringId)
+{
+ as->loadString(stringId);
+}
+
+void BaselineJIT::generate_LoadRegExp(int regExpId)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(regExpId, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_LoadClosure(int value)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(value, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_LoadName(int name)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index)
+{
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->globalGetter(l, engine);
+}
+
+void BaselineJIT::generate_LoadGlobalLookup(int index)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreNameSloppy(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreNameStrict(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadElement(int base, int index)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passRegAsArg(index, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadElementA(int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value)
+{
+ auto engine = f->internalClass->engine;
+ if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict())
+ engine->throwTypeError();
+}
+
+void BaselineJIT::generate_StoreElement(int base, int index)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passRegAsArg(index, 2);
+ as->passRegAsArg(base, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadProperty(int name, int base)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(name, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+void BaselineJIT::generate_LoadPropertyA(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(name, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base)
+{
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->getter(l, engine, base);
+}
+
+void BaselineJIT::generate_GetLookup(int index, int base)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passRegAsArg(base, 3);
+ as->passInt32AsArg(index, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_GetLookupA(int index)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(index, 2);
+ as->passFunctionAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value)
+{
+ auto engine = f->internalClass->engine;
+ if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict())
+ engine->throwTypeError();
+}
+
+void BaselineJIT::generate_StoreProperty(int name, int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(name, 2);
+ as->passRegAsArg(base, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ if (!l->setter(l, engine, base, value) && f->isStrict())
+ engine->throwTypeError();
+}
+
+void BaselineJIT::generate_SetLookup(int index, int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passRegAsArg(base, 2);
+ as->passInt32AsArg(index, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(propertyIndex, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlScopeObjectProperty, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(propertyIndex, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlContextObjectProperty, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(captureRequired, 3);
+ as->passInt32AsArg(propertyIndex, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlScopeObjectProperty, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(captureRequired, 3);
+ as->passInt32AsArg(propertyIndex, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContextObjectProperty, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadIdObject(int index, int base)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlIdObject, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallValue(int argc, int argv)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passRegAsArg(argv, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passInt32AsArg(name, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passInt32AsArg(lookupIndex, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passRegAsArg(index, 2);
+ as->passRegAsArg(base, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallName(int name, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passRegAsArg(argv, 2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(argc, 2);
+ as->passRegAsArg(argv, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passRegAsArg(argv, 2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_SetExceptionHandler(int offset)
+{
+ if (offset)
+ as->setExceptionHandler(instructionOffset() + offset);
+ else
+ as->clearExceptionHandler();
+}
+
+void BaselineJIT::generate_ThrowException()
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, Assembler::IgnoreResult);
+ as->gotoCatchException();
+}
+
+void BaselineJIT::generate_GetException() { as->getException(); }
+void BaselineJIT::generate_SetException() { as->setException(); }
+
+static void createCallContextHelper(Value *stack, CppStackFrame *frame)
+{
+ stack[CallData::Context] = ExecutionContext::newCallContext(frame);
+}
+
+void BaselineJIT::generate_CreateCallContext()
+{
+ as->prepareCallWithArgCount(2);
+ as->passCppFrameAsArg(1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(createCallContextHelper, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); }
+
+static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg)
+{
+ QV4::Value &accumulator = stack[CallData::Accumulator];
+ accumulator = accumulator.toObject(engine);
+ if (engine->hasException)
+ return;
+ stack[reg] = stack[CallData::Context];
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator);
+}
+
+void BaselineJIT::generate_PushWithContext(int reg)
+{
+ STORE_IP();
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(reg, 2);
+ as->passRegAsArg(0, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); }
+
+void BaselineJIT::generate_ForeachIteratorObject()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_ForeachNextPropertyName()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(1);
+ as->passAccumulatorAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName,
+ Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteMember(engine, base, member)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+void BaselineJIT::generate_DeleteMember(int member, int base)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(member, 2);
+ as->passRegAsArg(base, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteElement(engine, base, index)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+void BaselineJIT::generate_DeleteSubscript(int base, int index)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passRegAsArg(index, 2);
+ as->passRegAsArg(base, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+static ReturnedValue deleteNameHelper(QV4::Function *function, int name)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteName(engine, name)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+void BaselineJIT::generate_DeleteName(int name)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passFunctionAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_TypeofName(int name)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_TypeofValue()
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(varName, 2);
+ as->passInt32AsArg(isDeletable, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_DefineArray(int argc, int args)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(argc, 2);
+ as->passRegAsArg(args, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
+ int arrayGetterSetterCountAndFlags, int args)
+{
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4);
+ as->passInt32AsArg(arrayValueCount, 3);
+ as->passInt32AsArg(internalClassId, 2);
+ as->passRegAsArg(args, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator);
+}
+void BaselineJIT::generate_CreateMappedArgumentsObject()
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject,
+ Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::generate_CreateUnmappedArgumentsObject()
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject,
+ Assembler::ResultInAccumulator);
+}
+
+static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t)
+{
+ if (!t->isObject()) {
+ if (t->isNullOrUndefined()) {
+ *t = engine->globalObject->asReturnedValue();
+ } else {
+ *t = t->toObject(engine)->asReturnedValue();
+ }
+ }
+}
+
+void BaselineJIT::generate_ConvertThisToObject()
+{
+ as->prepareCallWithArgCount(2);
+ as->passRegAsArg(CallData::This, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult);
+ as->checkException();
+}
+
+void BaselineJIT::generate_Construct(int func, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passRegAsArg(argv, 2);
+ as->passRegAsArg(func, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
+
+void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
+void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
+void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); }
+void BaselineJIT::generate_CmpNeInt(int lhs) { as->cmpneInt(lhs); }
+void BaselineJIT::generate_CmpEq(int lhs) { as->cmpeq(lhs); }
+void BaselineJIT::generate_CmpNe(int lhs) { as->cmpne(lhs); }
+void BaselineJIT::generate_CmpGt(int lhs) { as->cmpgt(lhs); }
+void BaselineJIT::generate_CmpGe(int lhs) { as->cmpge(lhs); }
+void BaselineJIT::generate_CmpLt(int lhs) { as->cmplt(lhs); }
+void BaselineJIT::generate_CmpLe(int lhs) { as->cmple(lhs); }
+void BaselineJIT::generate_CmpStrictEqual(int lhs) { as->cmpStrictEqual(lhs); }
+void BaselineJIT::generate_CmpStrictNotEqual(int lhs) { as->cmpStrictNotEqual(lhs); }
+
+void BaselineJIT::generate_CmpIn(int lhs)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(lhs, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_in, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CmpInstanceOf(int lhs)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(lhs, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
+}
+
+void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
+}
+
+void BaselineJIT::generate_UNot() { as->unot(); }
+void BaselineJIT::generate_UPlus() { as->toNumber(); }
+void BaselineJIT::generate_UMinus() { as->uminus(); }
+void BaselineJIT::generate_UCompl() { as->ucompl(); }
+void BaselineJIT::generate_Increment() { as->inc(); }
+void BaselineJIT::generate_Decrement() { as->dec(); }
+void BaselineJIT::generate_Add(int lhs) { as->add(lhs); }
+
+void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); }
+void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); }
+void BaselineJIT::generate_BitXor(int lhs) { as->bitXor(lhs); }
+void BaselineJIT::generate_UShr(int lhs) { as->ushr(lhs); }
+void BaselineJIT::generate_Shr(int lhs) { as->shr(lhs); }
+void BaselineJIT::generate_Shl(int lhs) { as->shl(lhs); }
+
+void BaselineJIT::generate_BitAndConst(int rhs) { as->bitAndConst(rhs); }
+void BaselineJIT::generate_BitOrConst(int rhs) { as->bitOrConst(rhs); }
+void BaselineJIT::generate_BitXorConst(int rhs) { as->bitXorConst(rhs); }
+void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); }
+void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); }
+void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); }
+
+void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); }
+void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
+void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); }
+void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); }
+
+//void BaselineJIT::generate_BinopContext(int alu, int lhs)
+//{
+// auto engine = function->internalClass->engine;
+// void *op = engine->runtime.runtimeMethods[alu];
+// STORE_ACC();
+// as->passAccumulatorAsArg(2);
+// as->passRegAsArg(lhs, 1);
+// as->passEngineAsArg(0);
+// as->callRuntime("binopContext", op, Assembler::ResultInAccumulator);
+// as->checkException();
+//}
+
+void BaselineJIT::generate_LoadQmlContext(int result)
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, Assembler::ResultInAccumulator);
+ as->storeReg(result);
+}
+
+void BaselineJIT::generate_LoadQmlImportedScripts(int result)
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, Assembler::ResultInAccumulator);
+ as->storeReg(result);
+}
+
+void BaselineJIT::generate_LoadQmlSingleton(int name)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlSingleton, Assembler::ResultInAccumulator);
+}
+
+void BaselineJIT::startInstruction(Instr::Type /*instr*/)
+{
+ if (hasLabel())
+ as->addLabel(instructionOffset());
+}
+
+void BaselineJIT::endInstruction(Instr::Type instr)
+{
+ Q_UNUSED(instr);
+}
+
+#define MOTH_UNUSED_ARGS0()
+#define MOTH_UNUSED_ARGS1(arg) \
+ Q_UNUSED(arg);
+#define MOTH_UNUSED_ARGS2(arg1, arg2) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2);
+#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3);
+#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3); \
+ Q_UNUSED(arg4);
+
+#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
+ MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
+
+#define MOTH_BEGIN_INSTR(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
+ Q_UNUSED(base_ptr);
+
+#define MOTH_END_INSTR(instr) \
+ continue; \
+ }
+
+void BaselineJIT::collectLabelsInBytecode()
+{
+ MOTH_JUMP_TABLE;
+
+ const char *code = reinterpret_cast<const char *>(function->codeData);
+ const char *start = code;
+ const char *end = code + function->compiledFunction->codeSize;
+ while (code < end) {
+ MOTH_DISPATCH()
+ Q_UNREACHABLE();
+
+ MOTH_BEGIN_INSTR(LoadReg)
+ MOTH_END_INSTR(LoadReg)
+
+ MOTH_BEGIN_INSTR(StoreReg)
+ MOTH_END_INSTR(StoreReg)
+
+ MOTH_BEGIN_INSTR(MoveReg)
+ MOTH_END_INSTR(MoveReg)
+
+ MOTH_BEGIN_INSTR(LoadConst)
+ MOTH_END_INSTR(LoadConst)
+
+ MOTH_BEGIN_INSTR(LoadNull)
+ MOTH_END_INSTR(LoadNull)
+
+ MOTH_BEGIN_INSTR(LoadZero)
+ MOTH_END_INSTR(LoadZero)
+
+ MOTH_BEGIN_INSTR(LoadTrue)
+ MOTH_END_INSTR(LoadTrue)
+
+ MOTH_BEGIN_INSTR(LoadFalse)
+ MOTH_END_INSTR(LoadFalse)
+
+ MOTH_BEGIN_INSTR(LoadUndefined)
+ MOTH_END_INSTR(LoadUndefined)
+
+ MOTH_BEGIN_INSTR(LoadInt)
+ MOTH_END_INSTR(LoadInt)
+
+ MOTH_BEGIN_INSTR(MoveConst)
+ MOTH_END_INSTR(MoveConst)
+
+ MOTH_BEGIN_INSTR(LoadLocal)
+ MOTH_END_INSTR(LoadLocal)
+
+ MOTH_BEGIN_INSTR(StoreLocal)
+ MOTH_END_INSTR(StoreLocal)
+
+ MOTH_BEGIN_INSTR(LoadScopedLocal)
+ MOTH_END_INSTR(LoadScopedLocal)
+
+ MOTH_BEGIN_INSTR(StoreScopedLocal)
+ MOTH_END_INSTR(StoreScopedLocal)
+
+ MOTH_BEGIN_INSTR(LoadRuntimeString)
+ MOTH_END_INSTR(LoadRuntimeString)
+
+ MOTH_BEGIN_INSTR(LoadRegExp)
+ MOTH_END_INSTR(LoadRegExp)
+
+ MOTH_BEGIN_INSTR(LoadClosure)
+ MOTH_END_INSTR(LoadClosure)
+
+ MOTH_BEGIN_INSTR(LoadName)
+ MOTH_END_INSTR(LoadName)
+
+ MOTH_BEGIN_INSTR(LoadGlobalLookup)
+ MOTH_END_INSTR(LoadGlobalLookup)
+
+ MOTH_BEGIN_INSTR(StoreNameSloppy)
+ MOTH_END_INSTR(StoreNameSloppy)
+
+ MOTH_BEGIN_INSTR(StoreNameStrict)
+ MOTH_END_INSTR(StoreNameStrict)
+
+ MOTH_BEGIN_INSTR(LoadElement)
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(StoreElement)
+ MOTH_END_INSTR(StoreElement)
+
+ MOTH_BEGIN_INSTR(LoadProperty)
+ MOTH_END_INSTR(LoadProperty)
+
+ MOTH_BEGIN_INSTR(LoadPropertyA)
+ MOTH_END_INSTR(LoadElementA)
+
+ MOTH_BEGIN_INSTR(GetLookup)
+ MOTH_END_INSTR(GetLookup)
+
+ MOTH_BEGIN_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookupA)
+
+ MOTH_BEGIN_INSTR(StoreProperty)
+ MOTH_END_INSTR(StoreProperty)
+
+ MOTH_BEGIN_INSTR(SetLookup)
+ MOTH_END_INSTR(SetLookup)
+
+ MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
+ MOTH_END_INSTR(StoreScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
+ MOTH_END_INSTR(LoadScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(StoreContextObjectProperty)
+ MOTH_END_INSTR(StoreContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadContextObjectProperty)
+ MOTH_END_INSTR(LoadContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadIdObject)
+ MOTH_END_INSTR(LoadIdObject)
+
+ MOTH_BEGIN_INSTR(CallValue)
+ MOTH_END_INSTR(CallValue)
+
+ MOTH_BEGIN_INSTR(CallProperty)
+ MOTH_END_INSTR(CallProperty)
+
+ MOTH_BEGIN_INSTR(CallPropertyLookup)
+ MOTH_END_INSTR(CallPropertyLookup)
+
+ MOTH_BEGIN_INSTR(CallElement)
+ MOTH_END_INSTR(CallElement)
+
+ MOTH_BEGIN_INSTR(CallName)
+ MOTH_END_INSTR(CallName)
+
+ MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
+ MOTH_END_INSTR(CallPossiblyDirectEval)
+
+ MOTH_BEGIN_INSTR(CallGlobalLookup)
+ MOTH_END_INSTR(CallGlobalLookup)
+
+ MOTH_BEGIN_INSTR(SetExceptionHandler)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(SetExceptionHandler)
+
+ MOTH_BEGIN_INSTR(ThrowException)
+ MOTH_END_INSTR(ThrowException)
+
+ MOTH_BEGIN_INSTR(GetException)
+ MOTH_END_INSTR(HasException)
+
+ MOTH_BEGIN_INSTR(SetException)
+ MOTH_END_INSTR(SetExceptionFlag)
+
+ MOTH_BEGIN_INSTR(CreateCallContext)
+ MOTH_END_INSTR(CreateCallContext)
+
+ MOTH_BEGIN_INSTR(PushCatchContext)
+ MOTH_END_INSTR(PushCatchContext)
+
+ MOTH_BEGIN_INSTR(PushWithContext)
+ MOTH_END_INSTR(PushWithContext)
+
+ MOTH_BEGIN_INSTR(PopContext)
+ MOTH_END_INSTR(PopContext)
+
+ MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ MOTH_END_INSTR(ForeachIteratorObject)
+
+ MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ MOTH_END_INSTR(ForeachNextPropertyName)
+
+ MOTH_BEGIN_INSTR(DeleteMember)
+ MOTH_END_INSTR(DeleteMember)
+
+ MOTH_BEGIN_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteSubscript)
+
+ MOTH_BEGIN_INSTR(DeleteName)
+ MOTH_END_INSTR(DeleteName)
+
+ MOTH_BEGIN_INSTR(TypeofName)
+ MOTH_END_INSTR(TypeofName)
+
+ MOTH_BEGIN_INSTR(TypeofValue)
+ MOTH_END_INSTR(TypeofValue)
+
+ MOTH_BEGIN_INSTR(DeclareVar)
+ MOTH_END_INSTR(DeclareVar)
+
+ MOTH_BEGIN_INSTR(DefineArray)
+ MOTH_END_INSTR(DefineArray)
+
+ MOTH_BEGIN_INSTR(DefineObjectLiteral)
+ MOTH_END_INSTR(DefineObjectLiteral)
+
+ MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
+ MOTH_END_INSTR(CreateMappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(ConvertThisToObject)
+ MOTH_END_INSTR(ConvertThisToObject)
+
+ MOTH_BEGIN_INSTR(Construct)
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(Jump)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(Jump)
+
+ MOTH_BEGIN_INSTR(JumpTrue)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(JumpTrue)
+
+ MOTH_BEGIN_INSTR(JumpFalse)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(JumpFalse)
+
+ MOTH_BEGIN_INSTR(CmpEqNull)
+ MOTH_END_INSTR(CmpEqNull)
+
+ MOTH_BEGIN_INSTR(CmpNeNull)
+ MOTH_END_INSTR(CmpNeNull)
+
+ MOTH_BEGIN_INSTR(CmpEqInt)
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNeInt)
+ MOTH_END_INSTR(CmpNeInt)
+
+ MOTH_BEGIN_INSTR(CmpEq)
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNe)
+ MOTH_END_INSTR(CmpNe)
+
+ MOTH_BEGIN_INSTR(CmpGt)
+ MOTH_END_INSTR(CmpGt)
+
+ MOTH_BEGIN_INSTR(CmpGe)
+ MOTH_END_INSTR(CmpGe)
+
+ MOTH_BEGIN_INSTR(CmpLt)
+ MOTH_END_INSTR(CmpLt)
+
+ MOTH_BEGIN_INSTR(CmpLe)
+ MOTH_END_INSTR(CmpLe)
+
+ MOTH_BEGIN_INSTR(CmpStrictEqual)
+ MOTH_END_INSTR(CmpStrictEqual)
+
+ MOTH_BEGIN_INSTR(CmpStrictNotEqual)
+ MOTH_END_INSTR(CmpStrictNotEqual)
+
+ MOTH_BEGIN_INSTR(CmpIn)
+ MOTH_END_INSTR(CmpIn)
+
+ MOTH_BEGIN_INSTR(CmpInstanceOf)
+ MOTH_END_INSTR(CmpInstanceOf)
+
+ MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
+
+ MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
+ labels.push_back(code - start + offset);
+ MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
+
+ MOTH_BEGIN_INSTR(UNot)
+ MOTH_END_INSTR(UNot)
+
+ MOTH_BEGIN_INSTR(UPlus)
+ MOTH_END_INSTR(UPlus)
+
+ MOTH_BEGIN_INSTR(UMinus)
+ MOTH_END_INSTR(UMinus)
+
+ MOTH_BEGIN_INSTR(UCompl)
+ MOTH_END_INSTR(UCompl)
+
+ MOTH_BEGIN_INSTR(Increment)
+ MOTH_END_INSTR(PreIncrement)
+
+ MOTH_BEGIN_INSTR(Decrement)
+ MOTH_END_INSTR(PreDecrement)
+
+ MOTH_BEGIN_INSTR(Add)
+ MOTH_END_INSTR(Add)
+
+ MOTH_BEGIN_INSTR(BitAnd)
+ MOTH_END_INSTR(BitAnd)
+
+ MOTH_BEGIN_INSTR(BitOr)
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXor)
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShr)
+ MOTH_END_INSTR(UShr)
+
+ MOTH_BEGIN_INSTR(Shr)
+ MOTH_END_INSTR(Shr)
+
+ MOTH_BEGIN_INSTR(Shl)
+ MOTH_END_INSTR(Shl)
+
+ MOTH_BEGIN_INSTR(BitAndConst)
+ MOTH_END_INSTR(BitAndConst)
+
+ MOTH_BEGIN_INSTR(BitOrConst)
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXorConst)
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShrConst)
+ MOTH_END_INSTR(UShrConst)
+
+ MOTH_BEGIN_INSTR(ShrConst)
+ MOTH_END_INSTR(ShrConst)
+
+ MOTH_BEGIN_INSTR(ShlConst)
+ MOTH_END_INSTR(ShlConst)
+
+ MOTH_BEGIN_INSTR(Mul)
+ MOTH_END_INSTR(Mul)
+
+ MOTH_BEGIN_INSTR(Div)
+ MOTH_END_INSTR(Div)
+
+ MOTH_BEGIN_INSTR(Mod)
+ MOTH_END_INSTR(Mod)
+
+ MOTH_BEGIN_INSTR(Sub)
+ MOTH_END_INSTR(Sub)
+
+ MOTH_BEGIN_INSTR(Ret)
+ MOTH_END_INSTR(Ret)
+
+#ifndef QT_NO_QML_DEBUGGER
+ MOTH_BEGIN_INSTR(Debug)
+ MOTH_END_INSTR(Debug)
+#endif // QT_NO_QML_DEBUGGER
+
+ MOTH_BEGIN_INSTR(LoadQmlContext)
+ MOTH_END_INSTR(LoadQmlContext)
+
+ MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
+ MOTH_END_INSTR(LoadQmlImportedScripts)
+
+ MOTH_BEGIN_INSTR(LoadQmlSingleton)
+ MOTH_END_INSTR(LoadQmlSingleton)
+ }
+}
+#undef MOTH_BEGIN_INSTR
+#undef MOTH_END_INSTR
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h
new file mode 100644
index 0000000000..dfa2a79c48
--- /dev/null
+++ b/src/qml/jit/qv4jit_p.h
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4JIT_P_H
+#define QV4JIT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4function_p.h>
+#include <private/qv4instr_moth_p.h>
+
+//QT_REQUIRE_CONFIG(qml_jit);
+
+#define JIT_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define JIT_DEFINE_ARGS0()
+#define JIT_DEFINE_ARGS1(arg) \
+ int arg
+#define JIT_DEFINE_ARGS2(arg1, arg2) \
+ int arg1, \
+ int arg2
+#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1, \
+ int arg2, \
+ int arg3
+#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4
+
+#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
+ virtual void generate_##name( \
+ JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ ) = 0;
+
+#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
+ INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace JIT {
+
+class Assembler;
+
+class ByteCodeHandler
+{
+public:
+ virtual ~ByteCodeHandler();
+
+ void decode(const char *code, uint len);
+
+ int instructionOffset() const { return _offset; }
+
+protected:
+ FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+ virtual void startInstruction(Moth::Instr::Type instr) = 0;
+ virtual void endInstruction(Moth::Instr::Type instr) = 0;
+
+private:
+ int _offset = 0;
+};
+
+#ifdef V4_ENABLE_JIT
+class BaselineJIT final: public ByteCodeHandler
+{
+public:
+ BaselineJIT(QV4::Function *);
+ virtual ~BaselineJIT();
+
+ void generate();
+
+ void generate_Ret() Q_DECL_OVERRIDE;
+ void generate_Debug() Q_DECL_OVERRIDE;
+ void generate_LoadConst(int index) Q_DECL_OVERRIDE;
+ void generate_LoadZero() Q_DECL_OVERRIDE;
+ void generate_LoadTrue() Q_DECL_OVERRIDE;
+ void generate_LoadFalse() Q_DECL_OVERRIDE;
+ void generate_LoadNull() Q_DECL_OVERRIDE;
+ void generate_LoadUndefined() Q_DECL_OVERRIDE;
+ void generate_LoadInt(int value) Q_DECL_OVERRIDE;
+ void generate_MoveConst(int constIndex, int destTemp) Q_DECL_OVERRIDE;
+ void generate_LoadReg(int reg) Q_DECL_OVERRIDE;
+ void generate_StoreReg(int reg) Q_DECL_OVERRIDE;
+ void generate_MoveReg(int srcReg, int destReg) Q_DECL_OVERRIDE;
+ void generate_LoadLocal(int index) Q_DECL_OVERRIDE;
+ void generate_StoreLocal(int index) Q_DECL_OVERRIDE;
+ void generate_LoadScopedLocal(int scope, int index) Q_DECL_OVERRIDE;
+ void generate_StoreScopedLocal(int scope, int index) Q_DECL_OVERRIDE;
+ void generate_LoadRuntimeString(int stringId) Q_DECL_OVERRIDE;
+ void generate_LoadRegExp(int regExpId) Q_DECL_OVERRIDE;
+ void generate_LoadClosure(int value) Q_DECL_OVERRIDE;
+ void generate_LoadName(int name) Q_DECL_OVERRIDE;
+ void generate_LoadGlobalLookup(int index) Q_DECL_OVERRIDE;
+ void generate_StoreNameSloppy(int name) Q_DECL_OVERRIDE;
+ void generate_StoreNameStrict(int name) Q_DECL_OVERRIDE;
+ void generate_LoadElement(int base, int index) Q_DECL_OVERRIDE;
+ void generate_LoadElementA(int base) Q_DECL_OVERRIDE;
+ void generate_StoreElement(int base, int index) Q_DECL_OVERRIDE;
+ void generate_LoadProperty(int name, int base) Q_DECL_OVERRIDE;
+ void generate_LoadPropertyA(int name) Q_DECL_OVERRIDE;
+ void generate_GetLookup(int index, int base) Q_DECL_OVERRIDE;
+ void generate_GetLookupA(int index) Q_DECL_OVERRIDE;
+ void generate_StoreProperty(int name, int base) Q_DECL_OVERRIDE;
+ void generate_SetLookup(int index, int base) Q_DECL_OVERRIDE;
+ void generate_StoreScopeObjectProperty(int base,
+ int propertyIndex) Q_DECL_OVERRIDE;
+ void generate_StoreContextObjectProperty(int base,
+ int propertyIndex) Q_DECL_OVERRIDE;
+ void generate_LoadScopeObjectProperty(int propertyIndex, int base,
+ int captureRequired) Q_DECL_OVERRIDE;
+ void generate_LoadContextObjectProperty(int propertyIndex, int base,
+ int captureRequired) Q_DECL_OVERRIDE;
+ void generate_LoadIdObject(int index, int base) Q_DECL_OVERRIDE;
+ void generate_CallValue(int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallProperty(int name, int base, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallElement(int base, int index, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallName(int name, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallPossiblyDirectEval(int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_CallGlobalLookup(int index, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_SetExceptionHandler(int offset) Q_DECL_OVERRIDE;
+ void generate_ThrowException() Q_DECL_OVERRIDE;
+ void generate_GetException() Q_DECL_OVERRIDE;
+ void generate_SetException() Q_DECL_OVERRIDE;
+ void generate_CreateCallContext() Q_DECL_OVERRIDE;
+ void generate_PushCatchContext(int name, int reg) Q_DECL_OVERRIDE;
+ void generate_PushWithContext(int reg) Q_DECL_OVERRIDE;
+ void generate_PopContext(int reg) Q_DECL_OVERRIDE;
+ void generate_ForeachIteratorObject() Q_DECL_OVERRIDE;
+ void generate_ForeachNextPropertyName() Q_DECL_OVERRIDE;
+ void generate_DeleteMember(int member, int base) Q_DECL_OVERRIDE;
+ void generate_DeleteSubscript(int base, int index) Q_DECL_OVERRIDE;
+ void generate_DeleteName(int name) Q_DECL_OVERRIDE;
+ void generate_TypeofName(int name) Q_DECL_OVERRIDE;
+ void generate_TypeofValue() Q_DECL_OVERRIDE;
+ void generate_DeclareVar(int varName, int isDeletable) Q_DECL_OVERRIDE;
+ void generate_DefineArray(int argc, int args) Q_DECL_OVERRIDE;
+ void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
+ int arrayGetterSetterCountAndFlags,
+ int args) Q_DECL_OVERRIDE;
+ void generate_CreateMappedArgumentsObject() Q_DECL_OVERRIDE;
+ void generate_CreateUnmappedArgumentsObject() Q_DECL_OVERRIDE;
+ void generate_ConvertThisToObject() Q_DECL_OVERRIDE;
+ void generate_Construct(int func, int argc, int argv) Q_DECL_OVERRIDE;
+ void generate_Jump(int offset) Q_DECL_OVERRIDE;
+ void generate_JumpTrue(int offset) Q_DECL_OVERRIDE;
+ void generate_JumpFalse(int offset) Q_DECL_OVERRIDE;
+ void generate_CmpEqNull() Q_DECL_OVERRIDE;
+ void generate_CmpNeNull() Q_DECL_OVERRIDE;
+ void generate_CmpEqInt(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpNeInt(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpEq(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpNe(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpGt(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpGe(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpLt(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpLe(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpStrictEqual(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpStrictNotEqual(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpIn(int lhs) Q_DECL_OVERRIDE;
+ void generate_CmpInstanceOf(int lhs) Q_DECL_OVERRIDE;
+ void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs,
+ int offset) Q_DECL_OVERRIDE;
+ void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs,
+ int offset) Q_DECL_OVERRIDE;
+ void generate_UNot() Q_DECL_OVERRIDE;
+ void generate_UPlus() Q_DECL_OVERRIDE;
+ void generate_UMinus() Q_DECL_OVERRIDE;
+ void generate_UCompl() Q_DECL_OVERRIDE;
+ void generate_Increment() Q_DECL_OVERRIDE;
+ void generate_Decrement() Q_DECL_OVERRIDE;
+ void generate_Add(int lhs) Q_DECL_OVERRIDE;
+ void generate_BitAnd(int lhs) Q_DECL_OVERRIDE;
+ void generate_BitOr(int lhs) Q_DECL_OVERRIDE;
+ void generate_BitXor(int lhs) Q_DECL_OVERRIDE;
+ void generate_UShr(int lhs) Q_DECL_OVERRIDE;
+ void generate_Shr(int lhs) Q_DECL_OVERRIDE;
+ void generate_Shl(int lhs) Q_DECL_OVERRIDE;
+ void generate_BitAndConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_BitOrConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_BitXorConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_UShrConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_ShrConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_ShlConst(int rhs) Q_DECL_OVERRIDE;
+ void generate_Mul(int lhs) Q_DECL_OVERRIDE;
+ void generate_Div(int lhs) Q_DECL_OVERRIDE;
+ void generate_Mod(int lhs) Q_DECL_OVERRIDE;
+ void generate_Sub(int lhs) Q_DECL_OVERRIDE;
+ void generate_LoadQmlContext(int result) Q_DECL_OVERRIDE;
+ void generate_LoadQmlImportedScripts(int result) Q_DECL_OVERRIDE;
+ void generate_LoadQmlSingleton(int name) Q_DECL_OVERRIDE;
+
+ void startInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE;
+ void endInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE;
+
+protected:
+ bool hasLabel() const
+ { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); }
+
+private:
+ void collectLabelsInBytecode();
+
+private:
+ QV4::Function *function;
+ QScopedPointer<Assembler> as;
+ std::vector<int> labels;
+};
+#endif // V4_ENABLE_JIT
+
+} // namespace JIT
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4JIT_P_H
diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp
deleted file mode 100644
index cd2d53ab40..0000000000
--- a/src/qml/jit/qv4regalloc.cpp
+++ /dev/null
@@ -1,1971 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the V4VM module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QBuffer>
-#include <QtCore/QDebug>
-#include "qv4regalloc_p.h"
-#include "qv4alloca_p.h"
-#include <private/qv4value_p.h>
-
-#include <algorithm>
-#if defined(Q_CC_MINGW)
-# include <malloc.h>
-#endif
-
-namespace {
-enum { DebugRegAlloc = 0 };
-
-struct Use {
- enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 };
- unsigned flag : 1;
- unsigned pos : 31;
-
- Use(): flag(MustHaveRegister), pos(0) {}
- Use(int position, RegisterFlag flag): flag(flag), pos(position)
- { Q_ASSERT(position >= 0); }
-
- bool mustHaveRegister() const { return flag == MustHaveRegister; }
-};
-}
-
-QT_BEGIN_NAMESPACE
-
-Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE);
-
-using namespace QV4::IR;
-
-namespace QV4 {
-namespace JIT {
-
-namespace {
-class IRPrinterWithPositions: public IRPrinter
-{
- LifeTimeIntervals::Ptr intervals;
- const int positionSize;
-
-public:
- IRPrinterWithPositions(QTextStream *out, const LifeTimeIntervals::Ptr &intervals)
- : IRPrinter(out)
- , intervals(intervals)
- , positionSize(QString::number(intervals->lastPosition()).size())
- {}
-
-protected:
- void addStmtNr(Stmt *s) override final
- {
- addJustifiedNr(intervals->positionForStatement(s));
- }
-};
-
-class IRPrinterWithRegisters: public IRPrinterWithPositions
-{
- const RegisterInformation &_registerInformation;
- QHash<int, const RegisterInfo *> _infoForRegularRegister;
- QHash<int, const RegisterInfo *> _infoForFPRegister;
-
-public:
- IRPrinterWithRegisters(QTextStream *out, const LifeTimeIntervals::Ptr &intervals,
- const RegisterInformation &registerInformation)
- : IRPrinterWithPositions(out, intervals)
- , _registerInformation(registerInformation)
- {
- for (int i = 0, ei = _registerInformation.size(); i != ei; ++i)
- if (_registerInformation.at(i).isRegularRegister())
- _infoForRegularRegister.insert(_registerInformation.at(i).reg<int>(),
- &_registerInformation.at(i));
- else
- _infoForFPRegister.insert(_registerInformation.at(i).reg<int>(),
- &_registerInformation.at(i));
- }
-
-protected:
- void visitTemp(Temp *e) override final
- {
- switch (e->kind) {
- case Temp::PhysicalRegister: {
- const RegisterInfo *ri = e->type == DoubleType ? _infoForFPRegister.value(e->index, 0)
- : _infoForRegularRegister.value(e->index, 0);
- if (ri) {
- *out << ri->prettyName();
- break;
- }
- Q_FALLTHROUGH();
- }
- default:
- IRPrinterWithPositions::visitTemp(e);
- }
- }
-};
-}
-
-class RegAllocInfo: public IRDecoder
-{
-public:
- typedef QVarLengthArray<Temp, 4> Hints;
-
-private:
- struct Def {
- unsigned valid : 1;
- unsigned canHaveReg : 1;
- unsigned isPhiTarget : 1;
-
- Def(): valid(0), canHaveReg(0), isPhiTarget(0) {}
- Def(bool canHaveReg, bool isPhiTarget)
- : valid(1), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget)
- {
- }
-
- bool isValid() const { return valid != 0; }
- };
-
- IR::LifeTimeIntervals::Ptr _lifeTimeIntervals;
- BasicBlock *_currentBB;
- Stmt *_currentStmt;
- std::vector<Def> _defs;
- std::vector<std::vector<Use> > _uses;
- std::vector<int> _calls;
- std::vector<Hints> _hints;
-
- int usePosition(Stmt *s) const
- {
- int usePos = _lifeTimeIntervals->positionForStatement(s);
- if (usePos == Stmt::InvalidId) // phi-node operand, so:
- usePos = _lifeTimeIntervals->startPosition(_currentBB);
- return usePos;
- }
-
-public:
- RegAllocInfo(): _currentBB(0), _currentStmt(0) {}
-
- void collect(IR::Function *function, const IR::LifeTimeIntervals::Ptr &lifeTimeIntervals)
- {
- _lifeTimeIntervals = lifeTimeIntervals;
- _defs.resize(function->tempCount);
- _uses.resize(function->tempCount);
- _calls.reserve(function->statementCount() / 3);
- _hints.resize(function->tempCount);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- _currentBB = bb;
- for (Stmt *s : bb->statements()) {
- _currentStmt = s;
- visit(s);
- }
- }
- }
-
- const std::vector<Use> &uses(const Temp &t) const
- {
- return _uses.at(t.index);
- }
-
- bool canHaveRegister(const Temp &t) const {
- Q_ASSERT(_defs[t.index].isValid());
- return _defs[t.index].canHaveReg;
- }
- bool isPhiTarget(const Temp &t) const {
- Q_ASSERT(_defs[t.index].isValid());
- return _defs[t.index].isPhiTarget;
- }
-
- const std::vector<int> &calls() const { return _calls; }
- const Hints &hints(const Temp &t) const { return _hints[t.index]; }
- void addHint(const Temp &t, int physicalRegister)
- { addHint(t, Temp::PhysicalRegister, physicalRegister); }
-
- void addHint(const Temp &t, Temp::Kind kind, int hintedIndex)
- {
- Hints &hints = _hints[t.index];
- for (Hints::iterator i = hints.begin(), ei = hints.end(); i != ei; ++i)
- if (i->index == hintedIndex)
- return;
-
- Temp hint;
- hint.init(kind, hintedIndex);
- hints.append(hint);
- }
-
- void dump() const
- {
- if (!DebugRegAlloc)
- return;
-
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _lifeTimeIntervals);
-
- qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl;
- for (unsigned t = 0; t < _defs.size(); ++t) {
- const std::vector<Use> &uses = _uses[t];
- if (uses.empty())
- continue;
- qout << "%" << t <<": "
- << " ("
- << (_defs[t].canHaveReg ? "can" : "can NOT")
- << " have a register, and "
- << (_defs[t].isPhiTarget ? "is" : "is NOT")
- << " defined by a phi node), uses at: ";
- for (unsigned i = 0; i < uses.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << uses[i].pos;
- if (uses[i].mustHaveRegister()) qout << "(R)"; else qout << "(S)";
- }
- qout << endl;
- }
-
- qout << "Calls at: ";
- for (unsigned i = 0; i < _calls.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << _calls[i];
- }
- qout << endl;
-
- qout << "Hints:" << endl;
- for (unsigned t = 0; t < _hints.size(); ++t) {
- if (_uses[t].empty())
- continue;
- qout << "\t%" << t << ": ";
- const Hints &hints = _hints[t];
- for (int i = 0; i < hints.size(); ++i) {
- if (i > 0) qout << ", ";
- printer.print(hints[i]);
- }
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
-protected: // IRDecoder
- void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) override {}
- void callBuiltinTypeofQmlContextProperty(IR::Expr *, IR::Member::MemberKind, int, IR::Expr *) override {}
- void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) override {}
- void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {}
- void callBuiltinTypeofName(const QString &, IR::Expr *) override {}
- void callBuiltinTypeofValue(IR::Expr *, IR::Expr *) override {}
- void callBuiltinDeleteMember(IR::Expr *, const QString &, IR::Expr *) override {}
- void callBuiltinDeleteSubscript(IR::Expr *, IR::Expr *, IR::Expr *) override {}
- void callBuiltinDeleteName(const QString &, IR::Expr *) override {}
- void callBuiltinDeleteValue(IR::Expr *) override {}
- void callBuiltinThrow(IR::Expr *) override {}
- void callBuiltinReThrow() override {}
- void callBuiltinUnwindException(IR::Expr *) override {}
- void callBuiltinPushCatchScope(const QString &) override {};
- void callBuiltinForeachIteratorObject(IR::Expr *, IR::Expr *) override {}
- virtual void callBuiltinForeachNextProperty(IR::Temp *, IR::Temp *) {}
- void callBuiltinForeachNextPropertyname(IR::Expr *, IR::Expr *) override {}
- void callBuiltinPushWithScope(IR::Expr *) override {}
- void callBuiltinPopScope() override {}
- void callBuiltinDeclareVar(bool , const QString &) override {}
- void callBuiltinDefineArray(IR::Expr *, IR::ExprList *) override {}
- void callBuiltinDefineObjectLiteral(IR::Expr *, int, IR::ExprList *, IR::ExprList *, bool) override {}
- void callBuiltinSetupArgumentObject(IR::Expr *) override {}
- void callBuiltinConvertThisToObject() override {}
-
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- if (IR::Temp *tempValue = value->asTemp())
- addUses(tempValue, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex,
- IR::ExprList *args, IR::Expr *result) override
- {
- Q_UNUSED(propertyIndex)
-
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result) override
- {
- Q_UNUSED(name)
-
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
- IR::Expr *result) override
- {
- addDef(result);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(index->asTemp(), Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void convertType(IR::Expr *source, IR::Expr *target) override
- {
- addDef(target);
-
- bool needsCall = true;
- Use::RegisterFlag sourceReg = Use::CouldHaveRegister;
-
- switch (target->type) {
- case DoubleType:
- switch (source->type) {
- case UInt32Type:
- case SInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- break;
- default:
- break;
- }
- break;
- case BoolType:
- switch (source->type) {
- case UInt32Type:
- sourceReg = Use::MustHaveRegister;
- needsCall = false;
- break;
- case DoubleType:
- case UndefinedType:
- case NullType:
- case SInt32Type:
- needsCall = false;
- break;
- default:
- break;
- }
- break;
- case SInt32Type:
- switch (source->type) {
- case UInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- default:
- break;
- }
- break;
- case UInt32Type:
- switch (source->type) {
- case SInt32Type:
- case NullType:
- case UndefinedType:
- case BoolType:
- needsCall = false;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- Temp *sourceTemp = source->asTemp();
- if (sourceTemp)
- addUses(sourceTemp, sourceReg);
-
- if (needsCall)
- addCall();
- else if (target->asTemp())
- addHint(target->asTemp(), sourceTemp);
- }
-
- void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void constructProperty(IR::Expr *base, const QString &, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(base, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override
- {
- addDef(result);
- addUses(value, Use::CouldHaveRegister);
- addUses(args, Use::CouldHaveRegister);
- addCall();
- }
-
- void loadThisObject(IR::Expr *temp) override
- {
- addDef(temp);
- }
-
- void loadQmlContext(IR::Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void loadQmlImportedScripts(IR::Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void loadQmlSingleton(const QString &/*name*/, Expr *temp) override
- {
- Q_UNUSED(temp);
-
- addDef(temp);
- addCall();
- }
-
- void loadConst(IR::Const *sourceConst, Expr *targetTemp) override
- {
- Q_UNUSED(sourceConst);
-
- addDef(targetTemp);
- }
-
- void loadString(const QString &str, Expr *targetTemp) override
- {
- Q_UNUSED(str);
-
- addDef(targetTemp);
- }
-
- void loadRegexp(IR::RegExp *sourceRegexp, Expr *targetTemp) override
- {
- Q_UNUSED(sourceRegexp);
-
- addDef(targetTemp);
- addCall();
- }
-
- void getActivationProperty(const IR::Name *, Expr *temp) override
- {
- addDef(temp);
- addCall();
- }
-
- void setActivationProperty(IR::Expr *source, const QString &) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void initClosure(IR::Closure *closure, Expr *target) override
- {
- Q_UNUSED(closure);
-
- addDef(target);
- addCall();
- }
-
- void getProperty(IR::Expr *base, const QString &, Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase,
- IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/,
- bool /*captureRequired*/, IR::Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/,
- bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void getElement(IR::Expr *base, IR::Expr *index, Expr *target) override
- {
- addDef(target);
- addUses(base->asTemp(), Use::CouldHaveRegister);
- addUses(index->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override
- {
- addUses(source->asTemp(), Use::CouldHaveRegister);
- addUses(targetBase->asTemp(), Use::CouldHaveRegister);
- addUses(targetIndex->asTemp(), Use::CouldHaveRegister);
- addCall();
- }
-
- void copyValue(Expr *source, Expr *target) override
- {
- addDef(target);
- Temp *sourceTemp = source->asTemp();
- if (!sourceTemp)
- return;
- addUses(sourceTemp, Use::CouldHaveRegister);
- Temp *targetTemp = target->asTemp();
- if (targetTemp)
- addHint(targetTemp, sourceTemp);
- }
-
- void swapValues(Expr *, Expr *) override
- {
- // Inserted by the register allocator, so it cannot occur here.
- Q_UNREACHABLE();
- }
-
- void unop(AluOp oper, Expr *source, Expr *target) override
- {
- addDef(target);
-
- bool needsCall = true;
- if (oper == OpNot && source->type == IR::BoolType && target->type == IR::BoolType)
- needsCall = false;
-
-#if 0 // TODO: change masm to generate code
- switch (oper) {
- case OpIfTrue:
- case OpNot:
- case OpUMinus:
- case OpUPlus:
- case OpCompl:
- needsCall = sourceTemp->type & ~NumberType && sourceTemp->type != BoolType;
- break;
-
- case OpIncrement:
- case OpDecrement:
- default:
- Q_UNREACHABLE();
- }
-#endif
-
- IR::Temp *sourceTemp = source->asTemp();
- if (needsCall) {
- if (sourceTemp)
- addUses(sourceTemp, Use::CouldHaveRegister);
- addCall();
- } else {
- if (sourceTemp)
- addUses(sourceTemp, Use::MustHaveRegister);
- }
- }
-
- void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Expr *target) override
- {
- bool needsCall = true;
-
- if (oper == OpStrictEqual || oper == OpStrictNotEqual) {
- bool noCall = leftSource->type == NullType || rightSource->type == NullType
- || leftSource->type == UndefinedType || rightSource->type == UndefinedType
- || leftSource->type == BoolType || rightSource->type == BoolType;
- needsCall = !noCall;
- } else if (leftSource->type == DoubleType && rightSource->type == DoubleType) {
- if (oper == OpMul || oper == OpAdd || oper == OpDiv || oper == OpSub
- || (oper >= OpGt && oper <= OpStrictNotEqual)) {
- needsCall = false;
- }
- } else if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) {
- needsCall = false;
- } else if (oper == OpAdd || oper == OpMul || oper == OpSub
- || (oper >= OpGt && oper <= OpStrictNotEqual)) {
- if (leftSource->type == SInt32Type && rightSource->type == SInt32Type)
- needsCall = false;
- }
-
- addDef(target);
-
- if (needsCall) {
- addUses(leftSource->asTemp(), Use::CouldHaveRegister);
- addUses(rightSource->asTemp(), Use::CouldHaveRegister);
- addCall();
- } else {
- addUses(leftSource->asTemp(), Use::MustHaveRegister);
- addHint(target, leftSource->asTemp());
- addHint(target, rightSource->asTemp());
-
-#if CPU(X86) || CPU(X86_64)
- switch (oper) {
- // The rhs operand can be a memory address
- case OpAdd:
- case OpSub:
- case OpMul:
- case OpDiv:
-#if CPU(X86_64)
- if (leftSource->type == DoubleType || rightSource->type == DoubleType) {
- // well, on 64bit the doubles are mangled, so they must first be loaded in a register and demangled, so...:
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
- break;
- }
- Q_FALLTHROUGH();
-#endif
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- addUses(rightSource->asTemp(), Use::CouldHaveRegister);
- break;
-
- default:
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
- break;
- }
-#else
- addUses(rightSource->asTemp(), Use::MustHaveRegister);
-#endif
- }
- }
-
- void visitJump(IR::Jump *) override {}
- void visitCJump(IR::CJump *s) override
- {
- if (Temp *t = s->cond->asTemp()) {
-#if 0 // TODO: change masm to generate code
- addUses(t, Use::MustHaveRegister);
-#else
- addUses(t, Use::CouldHaveRegister);
- addCall();
-#endif
- } else if (Binop *b = s->cond->asBinop()) {
- binop(b->op, b->left, b->right, 0);
- } else if (s->cond->asConst()) {
- // TODO: SSA optimization for constant condition evaluation should remove this.
- // See also visitCJump() in masm.
- addCall();
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitRet(IR::Ret *s) override
- { addUses(s->expr->asTemp(), Use::CouldHaveRegister); }
-
- void visitPhi(IR::Phi *s) override
- {
- addDef(s->targetTemp, true);
- for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
- Expr *e = s->incoming.at(i);
- if (Temp *t = e->asTemp()) {
- // The actual use of an incoming value in a phi node is right before the terminator
- // of the other side of the incoming edge.
- const int usePos = _lifeTimeIntervals->positionForStatement(_currentBB->in.at(i)->terminator()) - 1;
- addUses(t, Use::CouldHaveRegister, usePos);
- addHint(s->targetTemp, t);
- addHint(t, s->targetTemp);
- }
- }
- }
-
-protected:
- void callBuiltin(IR::Call *c, IR::Expr *result) override
- {
- addDef(result);
- addUses(c->base, Use::CouldHaveRegister);
- addUses(c->args, Use::CouldHaveRegister);
- addCall();
- }
-
-private:
- void addDef(Expr *e, bool isPhiTarget = false)
- {
- if (!e)
- return;
- Temp *t = e->asTemp();
- if (!t)
- return;
- if (!t || t->kind != Temp::VirtualRegister)
- return;
- Q_ASSERT(!_defs[t->index].isValid());
- bool canHaveReg = true;
- switch (t->type) {
- case QObjectType:
- case VarType:
- case StringType:
- case UndefinedType:
- case NullType:
- canHaveReg = false;
- break;
- default:
- break;
- }
-
- _defs[t->index] = Def(canHaveReg, isPhiTarget);
- }
-
- void addUses(Expr *e, Use::RegisterFlag flag)
- {
- const int usePos = usePosition(_currentStmt);
- addUses(e, flag, usePos);
- }
-
- void addUses(Expr *e, Use::RegisterFlag flag, int usePos)
- {
- Q_ASSERT(usePos > 0);
- if (!e)
- return;
- Temp *t = e->asTemp();
- if (!t)
- return;
- if (t && t->kind == Temp::VirtualRegister)
- _uses[t->index].push_back(Use(usePos, flag));
- }
-
- void addUses(ExprList *l, Use::RegisterFlag flag)
- {
- for (ExprList *it = l; it; it = it->next)
- addUses(it->expr, flag);
- }
-
- void addCall()
- {
- _calls.push_back(usePosition(_currentStmt));
- }
-
- void addHint(Expr *hinted, Temp *hint1, Temp *hint2 = 0)
- {
- if (hinted)
- if (Temp *hintedTemp = hinted->asTemp())
- addHint(hintedTemp, hint1, hint2);
- }
-
- void addHint(Temp *hinted, Temp *hint1, Temp *hint2 = 0)
- {
- if (!hinted || hinted->kind != Temp::VirtualRegister)
- return;
- if (hint1 && hint1->kind == Temp::VirtualRegister && hinted->type == hint1->type)
- addHint(*hinted, Temp::VirtualRegister, hint1->index);
- if (hint2 && hint2->kind == Temp::VirtualRegister && hinted->type == hint2->type)
- addHint(*hinted, Temp::VirtualRegister, hint2->index);
- }
-};
-
-} // JIT namespace
-} // QV4 namespace
-QT_END_NAMESPACE
-
-QT_USE_NAMESPACE
-
-using namespace QT_PREPEND_NAMESPACE(QV4::JIT);
-using namespace QT_PREPEND_NAMESPACE(QV4::IR);
-using namespace QT_PREPEND_NAMESPACE(QV4);
-
-namespace {
-class ResolutionPhase
-{
- Q_DISABLE_COPY(ResolutionPhase)
-
- LifeTimeIntervals::Ptr _intervals;
- QVector<LifeTimeInterval *> _unprocessedReverseOrder;
- IR::Function *_function;
- const std::vector<int> &_assignedSpillSlots;
- std::vector<const LifeTimeInterval *> _liveIntervals;
- const QVector<const RegisterInfo *> &_intRegs;
- const QVector<const RegisterInfo *> &_fpRegs;
-
- Stmt *_currentStmt;
- std::vector<Move *> _loads;
- std::vector<Move *> _stores;
-
- std::vector<std::vector<const LifeTimeInterval *> > _liveAtStart;
- std::vector<std::vector<const LifeTimeInterval *> > _liveAtEnd;
-
-public:
- ResolutionPhase(QVector<LifeTimeInterval *> &&unprocessedReversedOrder,
- const LifeTimeIntervals::Ptr &intervals,
- IR::Function *function,
- const std::vector<int> &assignedSpillSlots,
- const QVector<const RegisterInfo *> &intRegs,
- const QVector<const RegisterInfo *> &fpRegs)
- : _intervals(intervals)
- , _unprocessedReverseOrder(unprocessedReversedOrder)
- , _function(function)
- , _assignedSpillSlots(assignedSpillSlots)
- , _intRegs(intRegs)
- , _fpRegs(fpRegs)
- , _currentStmt(0)
- {
- _liveAtStart.resize(function->basicBlockCount());
- _liveAtEnd.resize(function->basicBlockCount());
- }
-
- void run() {
- renumber();
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions(&qout, _intervals).print(_function);
- qDebug("%s", buf.data().constData());
- }
- resolve();
- }
-
-private:
- int defPosition(Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(Stmt *s) const
- {
- return _intervals->positionForStatement(s);
- }
-
- void renumber()
- {
- QVector<Stmt *> newStatements;
-
- for (BasicBlock *bb : _function->basicBlocks()) {
- _currentStmt = 0;
-
- QVector<Stmt *> statements = bb->statements();
- newStatements.reserve(bb->statements().size() + 7);
- newStatements.erase(newStatements.begin(), newStatements.end());
-
- cleanOldIntervals(_intervals->startPosition(bb));
- addNewIntervals(_intervals->startPosition(bb));
- _liveAtStart[bb->index()] = _liveIntervals;
-
- for (int i = 0, ei = statements.size(); i != ei; ++i) {
- _currentStmt = statements.at(i);
- _loads.clear();
- _stores.clear();
- if (_currentStmt->asTerminator())
- addNewIntervals(usePosition(_currentStmt));
- else
- addNewIntervals(defPosition(_currentStmt));
- visit(_currentStmt);
- for (Move *load : _loads)
- newStatements.append(load);
- if (_currentStmt->asPhi())
- newStatements.prepend(_currentStmt);
- else
- newStatements.append(_currentStmt);
- for (Move *store : _stores)
- newStatements.append(store);
- }
-
- cleanOldIntervals(_intervals->endPosition(bb));
- _liveAtEnd[bb->index()] = _liveIntervals;
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- os << "Intervals live at the start of L" << bb->index() << ":" << endl;
- if (_liveAtStart[bb->index()].empty())
- os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) {
- os << "\t";
- i->dump(os);
- os << endl;
- }
- os << "Intervals live at the end of L" << bb->index() << ":" << endl;
- if (_liveAtEnd[bb->index()].empty())
- os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) {
- os << "\t";
- i->dump(os);
- os << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
- bb->setStatements(newStatements);
- }
-
- }
-
- const LifeTimeInterval *findLiveInterval(Temp *t) const
- {
- for (const LifeTimeInterval *lti : _liveIntervals) {
- if (lti->temp() == *t)
- return lti;
- }
-
- return nullptr;
- }
-
- void maybeGenerateSpill(Temp *t)
- {
- const LifeTimeInterval *i = findLiveInterval(t);
- if (i->reg() == LifeTimeInterval::InvalidRegister)
- return;
-
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- int spillSlot = _assignedSpillSlots[i->temp().index];
- if (spillSlot != RegisterAllocator::InvalidSpillSlot)
- _stores.push_back(generateSpill(spillSlot, i->temp().type, pReg->reg<int>()));
- }
-
- void addNewIntervals(int position)
- {
- if (position == Stmt::InvalidId)
- return;
-
- while (!_unprocessedReverseOrder.isEmpty()) {
- const LifeTimeInterval *i = _unprocessedReverseOrder.constLast();
- if (i->start() > position)
- break;
-
- Q_ASSERT(!i->isFixedInterval());
- auto it = _liveIntervals.begin();
- for (; it != _liveIntervals.end(); ++it) {
- if ((*it)->temp() == i->temp()) {
- *it = i;
- break;
- }
- }
- if (it == _liveIntervals.end())
- _liveIntervals.push_back(i);
-// qDebug() << "-- Activating interval for temp" << i->temp().index;
-
- _unprocessedReverseOrder.removeLast();
- }
- }
-
- void cleanOldIntervals(int position)
- {
- for (size_t it = 0; it != _liveIntervals.size(); ) {
- const LifeTimeInterval *lti = _liveIntervals.at(it);
- if (lti->end() < position || lti->isFixedInterval())
- _liveIntervals.erase(_liveIntervals.begin() + it);
- else
- ++it;
- }
- }
-
- void resolve()
- {
- for (BasicBlock *bb : _function->basicBlocks()) {
- for (BasicBlock *bbOut : bb->out)
- resolveEdge(bb, bbOut);
- }
- }
-
- Phi *findDefPhi(const Temp &t, BasicBlock *bb) const
- {
- for (Stmt *s : bb->statements()) {
- Phi *phi = s->asPhi();
- if (!phi)
- return 0;
-
- if (*phi->targetTemp == t)
- return phi;
- }
-
- Q_UNREACHABLE();
- }
-
- void resolveEdge(BasicBlock *predecessor, BasicBlock *successor)
- {
- if (DebugRegAlloc) {
- qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _intervals);
- printer.print(predecessor);
- printer.print(successor);
- qDebug("%s", buf.data().constData());
- }
-
- MoveMapping mapping;
-
- const int predecessorEnd = _intervals->endPosition(predecessor);
- Q_ASSERT(predecessorEnd > 0);
-
- int successorStart = _intervals->startPosition(successor);
- Q_ASSERT(successorStart > 0);
-
- for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) {
- bool isPhiTarget = false;
- Expr *moveFrom = 0;
-
- if (it->start() == successorStart) {
- if (Phi *phi = findDefPhi(it->temp(), successor)) {
- isPhiTarget = true;
- Expr *opd = phi->incoming[successor->in.indexOf(predecessor)];
- if (opd->asConst()) {
- moveFrom = opd;
- } else {
- Temp *t = opd->asTemp();
- Q_ASSERT(t);
-
- for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) {
- if (it2->temp() == *t
- && it2->reg() != LifeTimeInterval::InvalidRegister
- && it2->covers(predecessorEnd)) {
- moveFrom = createPhysicalRegister(it2, t->type);
- break;
- }
- }
- if (!moveFrom)
- moveFrom = createTemp(Temp::StackSlot,
- _assignedSpillSlots[t->index],
- t->type);
- }
- }
- } else {
- for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) {
- if (predIt->temp() == it->temp()) {
- if (predIt->reg() != LifeTimeInterval::InvalidRegister
- && predIt->covers(predecessorEnd)) {
- moveFrom = createPhysicalRegister(predIt, predIt->temp().type);
- } else {
- int spillSlot = _assignedSpillSlots[predIt->temp().index];
- if (spillSlot != -1)
- moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type);
- }
- break;
- }
- }
- }
- if (!moveFrom) {
-#if !defined(QT_NO_DEBUG) && 0
- bool lifeTimeHole = false;
- if (it->ranges().first().start <= successorStart && it->ranges().last().end >= successorStart)
- lifeTimeHole = !it->covers(successorStart);
-
- Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole);
- if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) {
- const int successorEnd = successor->terminator()->id();
- const int idx = successor->in.indexOf(predecessor);
- for (const Use &use : _info->uses(it->temp)) {
- if (use.pos == static_cast<unsigned>(successorStart)) {
- // only check the current edge, not all other possible ones. This is
- // important for phi nodes: they have uses that are only valid when
- // coming in over a specific edge.
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- Q_ASSERT(it->temp().index != phi->targetTemp->index);
- Q_ASSERT(phi->d->incoming[idx]->asTemp() == 0
- || it->temp().index != phi->d->incoming[idx]->asTemp()->index);
- } else {
- // TODO: check that the first non-phi statement does not use
- // the temp.
- break;
- }
- }
- } else {
- Q_ASSERT(use.pos < static_cast<unsigned>(successorStart) ||
- use.pos > static_cast<unsigned>(successorEnd));
- }
- }
- }
-#endif
-
- continue;
- }
-
- Temp *moveTo;
- if (it->reg() == LifeTimeInterval::InvalidRegister || !it->covers(successorStart)) {
- if (!isPhiTarget) // if it->temp() is a phi target, skip it.
- continue;
- const int spillSlot = _assignedSpillSlots[it->temp().index];
- if (spillSlot == RegisterAllocator::InvalidSpillSlot)
- continue; // it has a life-time hole here.
- moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type);
- } else {
- moveTo = createPhysicalRegister(it, it->temp().type);
- }
-
- // add move to mapping
- mapping.add(moveFrom, moveTo);
- }
-
- if (DebugRegAlloc)
- mapping.dump();
- mapping.order();
- if (DebugRegAlloc)
- mapping.dump();
-
- bool insertIntoPredecessor = successor->in.size() > 1;
- mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function,
- insertIntoPredecessor);
-
- if (DebugRegAlloc) {
- qDebug() << ".. done, result:";
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _intervals);
- printer.print(predecessor);
- printer.print(successor);
- qDebug("%s", buf.data().constData());
- }
- }
-
- Temp *createTemp(Temp::Kind kind, int index, Type type) const
- {
- Q_ASSERT(index >= 0);
- Temp *t = _function->New<Temp>();
- t->init(kind, index);
- t->type = type;
- return t;
- }
-
- Temp *createPhysicalRegister(const LifeTimeInterval *i, Type type) const
- {
- const RegisterInfo *ri = platformRegister(*i);
- Q_ASSERT(ri);
- return createTemp(Temp::PhysicalRegister, ri->reg<int>(), type);
- }
-
- const RegisterInfo *platformRegister(const LifeTimeInterval &i) const
- {
- if (i.isFP())
- return _fpRegs.value(i.reg(), 0);
- else
- return _intRegs.value(i.reg(), 0);
- }
-
- Move *generateSpill(int spillSlot, Type type, int pReg) const
- {
- Q_ASSERT(spillSlot >= 0);
-
- Move *store = _function->NewStmt<Move>();
- store->init(createTemp(Temp::StackSlot, spillSlot, type),
- createTemp(Temp::PhysicalRegister, pReg, type));
- return store;
- }
-
- Move *generateUnspill(const Temp &t, int pReg) const
- {
- Q_ASSERT(pReg >= 0);
- int spillSlot = _assignedSpillSlots[t.index];
- Q_ASSERT(spillSlot != -1);
- Move *load = _function->NewStmt<Move>();
- load->init(createTemp(Temp::PhysicalRegister, pReg, t.type),
- createTemp(Temp::StackSlot, spillSlot, t.type));
- return load;
- }
-
-private:
- void visit(Expr *e)
- {
- switch (e->exprKind) {
- case Expr::TempExpr:
- visitTemp(e->asTemp());
- break;
- default:
- EXPR_VISIT_ALL_KINDS(e);
- break;
- }
- }
-
- void visitTemp(Temp *t)
- {
- if (t->kind != Temp::VirtualRegister)
- return;
-
- const LifeTimeInterval *i = findLiveInterval(t);
- Q_ASSERT(i->isValid());
-
- if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) {
- Q_ASSERT(i->isSplitFromInterval());
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- _loads.push_back(generateUnspill(i->temp(), pReg->reg<int>()));
- }
-
- if (i->reg() != LifeTimeInterval::InvalidRegister &&
- (i->covers(defPosition(_currentStmt)) ||
- i->covers(usePosition(_currentStmt)))) {
- const RegisterInfo *pReg = platformRegister(*i);
- Q_ASSERT(pReg);
- t->kind = Temp::PhysicalRegister;
- t->index = pReg->reg<unsigned>();
- } else {
- int stackSlot = _assignedSpillSlots[t->index];
- Q_ASSERT(stackSlot >= 0);
- t->kind = Temp::StackSlot;
- t->index = stackSlot;
- }
- }
-
- void visit(Stmt *s)
- {
- switch (s->stmtKind) {
- case Stmt::MoveStmt: {
- auto m = s->asMove();
- if (Temp *t = m->target->asTemp())
- maybeGenerateSpill(t);
-
- visit(m->source);
- visit(m->target);
- } break;
- case Stmt::PhiStmt: {
- auto p = s->asPhi();
- maybeGenerateSpill(p->targetTemp);
- } break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
- }
-};
-} // anonymous namespace
-
-RegisterAllocator::RegisterAllocator(const QV4::JIT::RegisterInformation &registerInformation)
- : _registerInformation(registerInformation)
-{
- for (int i = 0, ei = registerInformation.size(); i != ei; ++i) {
- const RegisterInfo &regInfo = registerInformation.at(i);
- if (regInfo.useForRegAlloc()) {
- if (regInfo.isRegularRegister())
- _normalRegisters.append(&regInfo);
- else
- _fpRegisters.append(&regInfo);
- }
- }
- Q_ASSERT(_normalRegisters.size() >= 2);
- Q_ASSERT(_fpRegisters.size() >= 2);
- _active.reserve((_normalRegisters.size() + _fpRegisters.size()) * 2);
- _inactive.reserve(_active.size());
-
- _regularRegsInUse.resize(_normalRegisters.size());
- _fpRegsInUse.resize(_fpRegisters.size());
-}
-
-RegisterAllocator::~RegisterAllocator()
-{
-}
-
-void RegisterAllocator::run(IR::Function *function, const Optimizer &opt)
-{
- _lastAssignedRegister.assign(function->tempCount, LifeTimeInterval::InvalidRegister);
- _assignedSpillSlots.assign(function->tempCount, InvalidSpillSlot);
- _activeSpillSlots.resize(function->tempCount);
-
- if (DebugRegAlloc)
- qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***";
-
- _lifeTimeIntervals = opt.lifeTimeIntervals();
-
- _unhandled = _lifeTimeIntervals->intervals();
- _handled.reserve(_unhandled.size());
-
- _info.reset(new RegAllocInfo);
- _info->collect(function, _lifeTimeIntervals);
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Ranges:" << endl;
- QVector<LifeTimeInterval *> intervals = _unhandled;
- std::reverse(intervals.begin(), intervals.end());
- for (const LifeTimeInterval *r : qAsConst(intervals)) {
- r->dump(qout);
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- _info->dump();
-
- qDebug() << "*** Before register allocation:";
- buf.setData(QByteArray());
- IRPrinterWithPositions(&qout, _lifeTimeIntervals).print(function);
- qDebug("%s", buf.data().constData());
- }
- prepareRanges();
-
- linearScan();
-
- if (DebugRegAlloc)
- dump(function);
-
- // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby
- // prevent the copy overhead that taking from the beginning would give).
- std::sort(_handled.begin(), _handled.end(),
- [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool {
- return LifeTimeInterval::lessThan(r2, r1);
- });
- ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run();
-
- function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1;
-
- if (DebugRegAlloc)
- qDebug() << "*** Finished regalloc , result:";
-
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (showCode) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithRegisters(&qout, _lifeTimeIntervals, _registerInformation).print(function);
- qDebug("%s", buf.data().constData());
- }
-}
-
-RegisterInformation RegisterAllocator::usedRegisters() const
-{
- RegisterInformation regInfo;
-
- for (int i = 0, ei = _normalRegisters.size(); i != ei; ++i) {
- if (_regularRegsInUse.testBit(i))
- regInfo.append(*_normalRegisters.at(i));
- }
-
- for (int i = 0, ei = _fpRegisters.size(); i != ei; ++i) {
- if (_fpRegsInUse.testBit(i))
- regInfo.append(*_fpRegisters.at(i));
- }
-
- return regInfo;
-}
-
-void RegisterAllocator::markInUse(int reg, bool isFPReg)
-{
- if (isFPReg)
- _fpRegsInUse.setBit(reg);
- else
- _regularRegsInUse.setBit(reg);
-}
-
-static inline LifeTimeInterval createFixedInterval(int rangeCount)
-{
- LifeTimeInterval i(rangeCount);
- i.setReg(0);
-
- Temp t;
- t.init(Temp::PhysicalRegister, 0);
- t.type = IR::SInt32Type;
- i.setTemp(t);
-
- return i;
-}
-
-LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original)
-{
- LifeTimeInterval *lti = new LifeTimeInterval(original);
- _lifeTimeIntervals->add(lti);
- lti->setReg(reg);
- lti->setFixedInterval(true);
-
- Temp t;
- t.init(Temp::PhysicalRegister, reg);
- t.type = isFP ? IR::DoubleType : IR::SInt32Type;
- lti->setTemp(t);
-
- return lti;
-}
-
-// Creates the intervals with fixed ranges. See [Wimmer2]. Note that this only applies to callee-
-// saved registers.
-void RegisterAllocator::prepareRanges()
-{
- LifeTimeInterval ltiWithCalls = createFixedInterval(int(_info->calls().size()));
- for (int callPosition : _info->calls())
- ltiWithCalls.addRange(callPosition, callPosition);
-
- const int regCount = _normalRegisters.size();
- _fixedRegisterRanges.resize(regCount);
- for (int reg = 0; reg < regCount; ++reg) {
- if (_normalRegisters.at(reg)->isCallerSaved()) {
- LifeTimeInterval *lti = cloneFixedInterval(reg, false, ltiWithCalls);
- if (lti->isValid()) {
- _fixedRegisterRanges[reg] = lti;
- _active.append(lti);
- }
- }
- }
-
- const int fpRegCount = _fpRegisters.size();
- _fixedFPRegisterRanges.resize(fpRegCount);
- for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) {
- if (_fpRegisters.at(fpReg)->isCallerSaved()) {
- LifeTimeInterval *lti = cloneFixedInterval(fpReg, true, ltiWithCalls);
- if (lti->isValid()) {
- _fixedFPRegisterRanges[fpReg] = lti;
- _active.append(lti);
- }
- }
- }
-}
-
-void RegisterAllocator::linearScan()
-{
- while (!_unhandled.isEmpty()) {
- LifeTimeInterval *current = _unhandled.back();
- _unhandled.pop_back();
- const int position = current->start();
-
- // check for intervals in active that are handled or inactive
- for (int i = 0; i < _active.size(); ) {
- LifeTimeInterval *it = _active.at(i);
- if (it->end() < position) {
- if (!it->isFixedInterval())
- _handled += it;
- _active.remove(i);
- } else if (!it->covers(position)) {
- _inactive += it;
- _active.remove(i);
- } else {
- ++i;
- }
- }
-
- // check for intervals in inactive that are handled or active
- for (int i = 0; i < _inactive.size(); ) {
- LifeTimeInterval *it = _inactive.at(i);
- if (it->end() < position) {
- if (!it->isFixedInterval())
- _handled += it;
- _inactive.remove(i);
- } else if (it->covers(position)) {
- if (it->reg() != LifeTimeInterval::InvalidRegister) {
- _active += it;
- _inactive.remove(i);
- } else {
- // although this interval is now active, it has no register allocated (always
- // spilled), so leave it in inactive.
- ++i;
- }
- } else {
- ++i;
- }
- }
-
- Q_ASSERT(!current->isFixedInterval());
-
-#ifdef DEBUG_REGALLOC
- qDebug() << "** Position" << position;
-#endif // DEBUG_REGALLOC
-
- if (_info->canHaveRegister(current->temp())) {
- tryAllocateFreeReg(*current);
- if (current->reg() == LifeTimeInterval::InvalidRegister)
- allocateBlockedReg(*current);
- if (current->reg() != LifeTimeInterval::InvalidRegister)
- _active += current;
- } else {
- assignSpillSlot(current->temp(), current->start(), current->end());
- _inactive += current;
- if (DebugRegAlloc)
- qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current->temp().index]
- << "for %" << current->temp().index << "as it cannot be loaded in a register";
- }
- }
-
- for (LifeTimeInterval *r : qAsConst(_active))
- if (!r->isFixedInterval())
- _handled.append(r);
- _active.clear();
- for (LifeTimeInterval *r : qAsConst(_inactive))
- if (!r->isFixedInterval())
- _handled.append(r);
- _inactive.clear();
-}
-
-static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &ranges, int position)
-{
- for (int i = 0, ei = ranges.size(); i != ei; ++i) {
- if (position <= ranges[i].end)
- return i;
- }
- return -1;
-}
-
-static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two)
-{
- if (one.covers(two.start))
- return two.start;
- if (two.covers(one.start))
- return one.start;
- return -1;
-}
-
-static inline bool isFP(const Temp &t)
-{ return t.type == DoubleType; }
-
-static inline bool candidateIsBetterFit(int bestSizeSoFar, int idealSize, int candidateSize)
-{
- // If the candidateSize is larger than the current we take it only if the current size does not
- // yet fit for the whole interval.
- if (bestSizeSoFar < candidateSize && bestSizeSoFar < idealSize)
- return true;
-
- // If the candidateSize is smaller we only take it if it still fits the whole interval.
- if (bestSizeSoFar > candidateSize && candidateSize >= idealSize)
- return true;
-
- // Other wise: no luck.
- return false;
-}
-
-// Out of all available registers (with their next-uses), choose the one that fits the requested
-// duration best. This can return a register that is not free for the whole interval, but that's
-// fine: we just have to split the current interval.
-static void longestAvailableReg(int *nextUses, int nextUseCount, int &reg, int &freeUntilPos_reg, int lastUse)
-{
- reg = LifeTimeInterval::InvalidRegister;
- freeUntilPos_reg = 0;
-
- for (int candidate = 0, candidateEnd = nextUseCount; candidate != candidateEnd; ++candidate) {
- int fp = nextUses[candidate];
- if (candidateIsBetterFit(freeUntilPos_reg, lastUse, fp)) {
- reg = candidate;
- freeUntilPos_reg = fp;
- }
- }
-}
-
-#define CALLOC_ON_STACK(ty, ptr, sz, val) \
- Q_ASSERT(sz > 0); \
- Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \
- for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \
- *it = val;
-
-// Try to allocate a register that's currently free.
-void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval &current)
-{
- Q_ASSERT(!current.isFixedInterval());
- Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister);
-
- const bool needsFPReg = isFP(current.temp());
- const int freeUntilPosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size();
- CALLOC_ON_STACK(int, freeUntilPos, freeUntilPosCount, INT_MAX);
-
- for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) {
- const LifeTimeInterval *it = *i;
- if (it->isFP() == needsFPReg)
- freeUntilPos[it->reg()] = 0; // mark register as unavailable
- }
-
- for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) {
- const LifeTimeInterval *it = *i;
- if (it->isFP() != needsFPReg)
- continue; // different register type, so not applicable.
- if (it->reg() == LifeTimeInterval::InvalidRegister)
- continue; // this range does not block a register from being used, as it has no register assigned
-
- if (current.isSplitFromInterval() || it->isFixedInterval()) {
- const int intersectionPos = nextIntersection(current, *it);
- if (intersectionPos != -1)
- freeUntilPos[it->reg()] = qMin(freeUntilPos[it->reg()], intersectionPos);
- }
- }
-
- int reg = LifeTimeInterval::InvalidRegister;
- int freeUntilPos_reg = 0;
-
- const RegAllocInfo::Hints &hints = _info->hints(current.temp());
- for (RegAllocInfo::Hints::const_iterator i = hints.begin(), ei = hints.end(); i != ei; ++i) {
- const Temp &hint = *i;
- int candidate;
- if (hint.kind == Temp::PhysicalRegister)
- candidate = hint.index;
- else
- candidate = _lastAssignedRegister[hint.index];
-
- const int end = current.end();
- if (candidate == LifeTimeInterval::InvalidRegister)
- continue; // the candidate has no register assigned, so it cannot be (re-)used
- if (current.isFP() != isFP(hint))
- continue; // different register type, so not applicable.
-
- const int fp = freeUntilPos[candidate];
- if (candidateIsBetterFit(freeUntilPos_reg, end, fp)) {
- reg = candidate;
- freeUntilPos_reg = fp;
- }
- }
-
- // None of the hinted registers could fit the interval, so try all registers next.
- if (reg == LifeTimeInterval::InvalidRegister)
- longestAvailableReg(freeUntilPos, freeUntilPosCount, reg, freeUntilPos_reg, current.end());
-
- if (freeUntilPos_reg == 0) {
- // no register available without spilling
- if (DebugRegAlloc)
- qDebug("*** no register available for %u", current.temp().index);
- return;
- } else if (current.end() < freeUntilPos_reg) {
- // register available for the whole interval
- if (DebugRegAlloc)
- qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index;
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- markInUse(reg, needsFPReg);
- } else {
- // register available for the first part of the interval
-
- // TODO: this is slightly inefficient in the following case:
- // %1 = something
- // some_call(%1)
- // %2 = %1 + 1
- // Now %1 will get a register assigned, and will be spilled to the stack immediately. It
- // would be better to check if there are actually uses in the range before the split.
-
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- if (DebugRegAlloc)
- qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index;
- split(current, freeUntilPos_reg, true);
- markInUse(reg, needsFPReg);
- }
-}
-
-// This gets called when all registers are currently in use.
-void RegisterAllocator::allocateBlockedReg(LifeTimeInterval &current)
-{
- Q_ASSERT(!current.isFixedInterval());
- Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister);
- const int position = current.start();
-
- const bool isPhiTarget = _info->isPhiTarget(current.temp());
- if (isPhiTarget && !current.isSplitFromInterval()) {
- // Special case: storing to a phi-node's target will result in a single move. So, if we
- // would spill another interval to the stack (that's 1 store), and then do the move for the
- // phi target (at least 1 move or a load), that would result in 2 instructions. Instead, we
- // force the phi-node's target to go to the stack immediately, which is always a single
- // store.
- split(current, position + 1, true);
- _inactive.append(&current);
- return;
- }
-
- const bool needsFPReg = isFP(current.temp());
- const int nextUsePosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size();
- CALLOC_ON_STACK(int, nextUsePos, nextUsePosCount, INT_MAX);
- QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePosCount, 0);
-
- for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) {
- LifeTimeInterval &it = **i;
- if (it.isFP() != needsFPReg)
- continue; // different register type, so not applicable.
-
- const int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.start());
- if (nu == position) {
- nextUsePos[it.reg()] = 0;
- } else if (nu != -1 && nu < nextUsePos[it.reg()]) {
- nextUsePos[it.reg()] = nu;
- nextUseRangeForReg[it.reg()] = &it;
- } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) {
- // in a loop, the range can be active, but the result might only be used before the
- // current position (e.g. the induction variable being used in the phi node in the loop
- // header). So, we can use this register, but we need to remember to split the interval
- // in order to have the edge-resolving generate a load at the edge going back to the
- // loop header.
- nextUseRangeForReg[it.reg()] = &it;
- }
- }
-
- for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) {
- LifeTimeInterval &it = **i;
- if (it.isFP() != needsFPReg)
- continue; // different register type, so not applicable.
- if (it.reg() == LifeTimeInterval::InvalidRegister)
- continue; // this range does not block a register from being used, as it has no register assigned
-
- if (current.isSplitFromInterval() || it.isFixedInterval()) {
- if (nextIntersection(current, it) != -1) {
- const int nu = nextUse(it.temp(), current.start());
- if (nu != -1 && nu < nextUsePos[it.reg()]) {
- nextUsePos[it.reg()] = nu;
- nextUseRangeForReg[it.reg()] = &it;
- }
- }
- }
- }
-
- int reg, nextUsePos_reg;
- longestAvailableReg(nextUsePos, nextUsePosCount, reg, nextUsePos_reg, current.end());
-
- Q_ASSERT(current.start() <= nextUsePos_reg);
-
- // spill interval that currently block reg
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "*** spilling intervals that block reg " <<reg<< " for interval ";
- current.dump(out);
- qDebug("%s", buf.data().constData());
- }
- current.setReg(reg);
- _lastAssignedRegister[current.temp().index] = reg;
- LifeTimeInterval *nextUse = nextUseRangeForReg[reg];
- Q_ASSERT(nextUse);
- Q_ASSERT(!nextUse->isFixedInterval());
-
- split(*nextUse, position, /*skipOptionalRegisterUses =*/ true);
-
- // We might have chosen a register that is used by a range that has a hole in its life time.
- // If that's the case, check if the current interval completely fits in the hole. Or rephrased:
- // check if the current interval will use the register after that hole ends (so that range, and
- // if so, split that interval so that it gets a new register assigned when it needs one.
- splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position);
-
- // make sure that current does not intersect with the fixed interval for reg
- const LifeTimeInterval *fixedRegRange = needsFPReg ? _fixedFPRegisterRanges.at(reg)
- : _fixedRegisterRanges.at(reg);
- if (fixedRegRange) {
- int ni = nextIntersection(current, *fixedRegRange);
- if (ni != -1) {
- if (DebugRegAlloc) {
- qDebug("***-- current range intersects with a fixed reg use at %d, so splitting it.", ni);
- }
- // current does overlap with a fixed interval, so split current before that intersection.
- split(current, ni, true);
- }
- }
-}
-
-int RegisterAllocator::nextIntersection(const LifeTimeInterval &current,
- const LifeTimeInterval &another) const
-{
- const LifeTimeInterval::Ranges &currentRanges = current.ranges();
- int currentIt = 0;
-
- const LifeTimeInterval::Ranges &anotherRanges = another.ranges();
- const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, current.start());
- if (anotherItStart == -1)
- return -1;
-
- for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) {
- const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt);
- for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) {
- const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt);
- if (anotherRange.start > currentRange.end)
- break;
- int intersectPos = intersectionPosition(currentRange, anotherRange);
- if (intersectPos != -1)
- return intersectPos;
- }
- }
-
- return -1;
-}
-
-/// Find the first use after the start position for the given temp.
-///
-/// This is only called when all registers are in use, and when one of them has to be spilled to the
-/// stack. So, uses where a register is optional can be ignored.
-int RegisterAllocator::nextUse(const Temp &t, int startPosition) const
-{
- typedef std::vector<Use>::const_iterator ConstIt;
-
- const std::vector<Use> &usePositions = _info->uses(t);
- const ConstIt cend = usePositions.end();
- for (ConstIt it = usePositions.begin(); it != cend; ++it) {
- if (it->mustHaveRegister()) {
- const int usePos = it->pos;
- if (usePos >= startPosition)
- return usePos;
- }
- }
-
- return -1;
-}
-
-static inline void insertReverseSorted(QVector<LifeTimeInterval *> &intervals, LifeTimeInterval *newInterval)
-{
- newInterval->validate();
- for (int i = intervals.size(); i > 0;) {
- if (LifeTimeInterval::lessThan(newInterval, intervals.at(--i))) {
- intervals.insert(i + 1, newInterval);
- return;
- }
- }
- intervals.insert(0, newInterval);
-}
-
-void RegisterAllocator::split(LifeTimeInterval &current, int beforePosition,
- bool skipOptionalRegisterUses)
-{ // TODO: check if we can always skip the optional register uses
- Q_ASSERT(!current.isFixedInterval());
-
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "***** split request for range ";
- current.dump(out);
- out << " before position " << beforePosition
- << " and skipOptionalRegisterUses = " << skipOptionalRegisterUses << endl;
- qDebug("%s", buf.data().constData());
- }
-
- assignSpillSlot(current.temp(), current.start(), current.end());
-
- const int firstPosition = current.start();
- Q_ASSERT(beforePosition > firstPosition && "split before start");
-
- int lastUse = firstPosition;
- int nextUse = -1;
- const std::vector<Use> &usePositions = _info->uses(current.temp());
- for (size_t i = 0, ei = usePositions.size(); i != ei; ++i) {
- const Use &usePosition = usePositions.at(i);
- const int usePos = usePosition.pos;
- if (lastUse < usePos && usePos < beforePosition) {
- lastUse = usePos;
- } else if (usePos >= beforePosition) {
- if (!skipOptionalRegisterUses || usePosition.mustHaveRegister()) {
- nextUse = usePos;
- break;
- }
- }
- }
- Q_ASSERT(lastUse != -1);
- Q_ASSERT(lastUse < beforePosition);
-
- LifeTimeInterval newInterval = current.split(lastUse, nextUse);
- if (DebugRegAlloc) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream out(&buf);
- out << "***** last use = " << lastUse << ", nextUse = " << nextUse << endl;
- out << "***** new interval: ";
- newInterval.dump(out);
- out << endl;
- out << "***** preceding interval: ";
- current.dump(out);
- out << endl;
- qDebug("%s", buf.data().constData());
- }
- if (newInterval.isValid()) {
- if (current.reg() != LifeTimeInterval::InvalidRegister)
- _info->addHint(current.temp(), current.reg());
- newInterval.setReg(LifeTimeInterval::InvalidRegister);
- LifeTimeInterval *newIntervalPtr = new LifeTimeInterval(newInterval);
- _lifeTimeIntervals->add(newIntervalPtr);
- insertReverseSorted(_unhandled, newIntervalPtr);
- }
-}
-
-void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position)
-{
- for (int i = 0, ei = _inactive.size(); i != ei; ++i) {
- LifeTimeInterval &interval = *_inactive[i];
- if (interval.isFixedInterval())
- continue;
- if (isFPReg == interval.isFP() && interval.reg() == reg) {
- LifeTimeInterval::Ranges ranges = interval.ranges();
- int endOfLifetimeHole = -1;
- for (int j = 0, ej = ranges.size(); j != ej; ++j) {
- if (position < ranges[j].start)
- endOfLifetimeHole = ranges[j].start;
- }
- if (endOfLifetimeHole != -1)
- split(interval, endOfLifetimeHole);
- }
- }
-}
-
-void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos)
-{
- if (_assignedSpillSlots[t.index] != InvalidSpillSlot)
- return;
-
- for (int i = 0, ei = _activeSpillSlots.size(); i != ei; ++i) {
- if (_activeSpillSlots.at(i) < startPos) {
- _activeSpillSlots[i] = endPos;
- _assignedSpillSlots[t.index] = i;
- return;
- }
- }
-
- Q_UNREACHABLE();
-}
-
-void RegisterAllocator::dump(IR::Function *function) const
-{
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinterWithPositions printer(&qout, _lifeTimeIntervals);
-
- qout << "Ranges:" << endl;
- QVector<LifeTimeInterval *> handled = _handled;
- std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp);
- for (const LifeTimeInterval *r : qAsConst(handled)) {
- r->dump(qout);
- qout << endl;
- }
-
- qout << "Spill slots:" << endl;
- for (unsigned i = 0; i < _assignedSpillSlots.size(); ++i)
- if (_assignedSpillSlots[i] != InvalidSpillSlot)
- qout << "\t%" << i << " -> " << _assignedSpillSlots[i] << endl;
-
- printer.print(function);
- qDebug("%s", buf.data().constData());
-}
-
-// References:
-// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO'10, ACM Press, 2010
-// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
-// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132-141. ACM Press, 2005.
-// [Traub] Omri Traub, Glenn Holloway, and Michael D. Smith. Quality and Speed in Linear-scan
-// Register Allocation. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming
-// Language Design and Implementation, pages 142-151, June 1998.
diff --git a/src/qml/jit/qv4regalloc_p.h b/src/qml/jit/qv4regalloc_p.h
deleted file mode 100644
index 4ee440b73e..0000000000
--- a/src/qml/jit/qv4regalloc_p.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the V4VM module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4REGALLOC_P_H
-#define QV4REGALLOC_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qv4global_p.h"
-#include "qv4isel_p.h"
-#include "qv4ssa_p.h"
-#include "qv4registerinfo_p.h"
-
-#include <config.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace JIT {
-
-class RegAllocInfo;
-
-// This class implements a linear-scan register allocator, with a couple of tweaks:
-// - Second-chance allocation is used when an interval becomes active after a spill, which results
-// in fewer differences between edges, and hence fewer moves before jumps.
-// - Use positions are flagged with either "must have" register or "could have" register. This is
-// used to decide whether a register is really needed when a temporary is used after a spill
-// occurred.
-// - Fixed intervals are used to denotate IR positions where certain registers are needed in order
-// to implement the operation, and cannot be used by a temporary on that position. An example is
-// caller saved registers, where the call will use/clobber those registers.
-// - Hints are used to indicate which registers could be used to generate more compact code. An
-// example is an addition, where one (or both) operands' life-time ends at that instruction. In
-// this case, re-using an operand register for the result will result in an in-place add.
-// - SSA form properties are used:
-// - to simplify life-times (two temporaries will never interfere as long as their intervals
-// are not split), resulting in a slightly faster algorithm;
-// - when a temporary needs to be spilled, it is done directly after calculating it, so that
-// 1 store is generated even if multiple spills/splits happen.
-// - phi-node elimination (SSA form deconstruction) is done when resolving differences between
-// CFG edges
-class RegisterAllocator
-{
- typedef IR::LifeTimeInterval LifeTimeInterval;
-
- const RegisterInformation &_registerInformation;
- QVector<const RegisterInfo *> _normalRegisters;
- QVector<const RegisterInfo *> _fpRegisters;
- QScopedPointer<RegAllocInfo> _info;
-
- QVector<LifeTimeInterval *> _fixedRegisterRanges, _fixedFPRegisterRanges;
-
- IR::LifeTimeIntervals::Ptr _lifeTimeIntervals;
- typedef QVector<LifeTimeInterval *> Intervals;
- Intervals _unhandled, _active, _inactive, _handled;
-
- std::vector<int> _lastAssignedRegister;
- std::vector<int> _assignedSpillSlots;
- QVector<int> _activeSpillSlots;
-
- QBitArray _regularRegsInUse, _fpRegsInUse;
-
- Q_DISABLE_COPY(RegisterAllocator)
-
-public:
- enum { InvalidSpillSlot = -1 };
-
- RegisterAllocator(const RegisterInformation &registerInformation);
- ~RegisterAllocator();
-
- void run(IR::Function *function, const IR::Optimizer &opt);
- RegisterInformation usedRegisters() const;
-
-private:
- void markInUse(int reg, bool isFPReg);
- LifeTimeInterval *cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original);
- void prepareRanges();
- void linearScan();
- void tryAllocateFreeReg(LifeTimeInterval &current);
- void allocateBlockedReg(LifeTimeInterval &current);
- int nextIntersection(const LifeTimeInterval &current, const LifeTimeInterval &another) const;
- int nextUse(const IR::Temp &t, int startPosition) const;
- void split(LifeTimeInterval &current, int beforePosition, bool skipOptionalRegisterUses =false);
- void splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position);
- void assignSpillSlot(const IR::Temp &t, int startPos, int endPos);
- void resolve(IR::Function *function, const IR::Optimizer &opt);
-
- void dump(IR::Function *function) const;
-};
-
-} // end of namespace JIT
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4REGALLOC_P_H
diff --git a/src/qml/jit/qv4registerinfo_p.h b/src/qml/jit/qv4registerinfo_p.h
deleted file mode 100644
index 214206db91..0000000000
--- a/src/qml/jit/qv4registerinfo_p.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4REGISTERINFO_P_H
-#define QV4REGISTERINFO_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QString>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace JIT {
-
-class RegisterInfo
-{
-public:
- enum { InvalidRegister = (1 << 29) - 1 };
- enum SavedBy { CallerSaved, CalleeSaved };
- enum RegisterType { RegularRegister, FloatingPointRegister };
- enum Usage { Predefined, RegAlloc };
-
-public:
- RegisterInfo()
- : _savedBy(CallerSaved)
- , _usage(Predefined)
- , _type(RegularRegister)
- , _reg(InvalidRegister)
- {}
-
- RegisterInfo(int reg, const QString &prettyName, RegisterType type, SavedBy savedBy, Usage usage)
- : _prettyName(prettyName)
- , _savedBy(savedBy)
- , _usage(usage)
- , _type(type)
- , _reg(reg)
- {}
-
- bool operator==(const RegisterInfo &other) const
- { return _type == other._type && _reg == other._reg; }
-
- bool isValid() const { return _reg != InvalidRegister; }
- template <typename T> T reg() const { return static_cast<T>(_reg); }
- QString prettyName() const { return _prettyName; }
- bool isCallerSaved() const { return _savedBy == CallerSaved; }
- bool isCalleeSaved() const { return _savedBy == CalleeSaved; }
- bool isFloatingPoint() const { return _type == FloatingPointRegister; }
- bool isRegularRegister() const { return _type == RegularRegister; }
- bool useForRegAlloc() const { return _usage == RegAlloc; }
- bool isPredefined() const { return _usage == Predefined; }
-
-private:
- QString _prettyName;
- unsigned _savedBy : 1;
- unsigned _usage : 1;
- unsigned _type : 1;
- unsigned _reg : 29;
-};
-typedef QVector<RegisterInfo> RegisterInformation;
-
-} // JIT namespace
-} // QV4 namespace
-
-QT_END_NAMESPACE
-
-#endif // QV4REGISTERINFO_P_H
diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h
deleted file mode 100644
index 6d788f4a93..0000000000
--- a/src/qml/jit/qv4targetplatform_p.h
+++ /dev/null
@@ -1,707 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4TARGETPLATFORM_P_H
-#define QV4TARGETPLATFORM_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <config.h>
-
-#if ENABLE(ASSEMBLER)
-
-#include <private/qv4value_p.h>
-#include "qv4registerinfo_p.h"
-#include <assembler/MacroAssembler.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace JIT {
-
-enum TargetOperatingSystemSpecialization {
- NoOperatingSystemSpecialization,
- WindowsSpecialization
-};
-
-// The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination.
-//
-// All combinations have a separate definition, guarded by #ifdefs. The exceptions are:
-// - Linux/x86 and win32, which are the same, except that linux non-PIC/PIE code does not need to
-// restore ebx (which holds the GOT ptr) before a call
-// - All (supported) ARM platforms, where the only variety is the platform specific usage of r9,
-// and the frame-pointer in Thumb/Thumb2 v.s. ARM mode.
-//
-// Specific handling of ebx when it holds the GOT:
-// In this case we can use it, but it needs to be restored when doing a call. So, the handling is as
-// follows: it is marked as caller saved, meaning the value in it won't survive a call. When
-// calculating the list of callee saved registers in getCalleeSavedRegisters (which is used to
-// generate push/pop instructions in the prelude/postlude), we add ebx too. Then when synthesizing
-// a call, we add a load it right before emitting the call instruction.
-//
-// NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h!
-template <typename PlatformAssembler, TargetOperatingSystemSpecialization specialization = NoOperatingSystemSpecialization>
-class TargetPlatform
-{
-};
-
-#if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS))
-template <>
-class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerX86;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
-
- enum { RegAllocIsSupported = 1 };
-
- static const RegisterID FramePointerRegister = JSC::X86Registers::ebp;
- static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
- static const RegisterID LocalsRegister = JSC::X86Registers::edi;
- static const RegisterID EngineRegister = JSC::X86Registers::esi;
- static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
- static const RegisterID ScratchRegister = JSC::X86Registers::ecx;
- static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
- static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1;
- static const RegisterID LowReturnValueRegister = JSC::X86Registers::eax;
- static const RegisterID HighReturnValueRegister = JSC::X86Registers::edx;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::esi, QStringLiteral("esi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-# define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
- static const int RegisterSize = 4;
-
- static const int RegisterArgumentCount = 0;
- static RegisterID registerForArgument(int) { Q_UNREACHABLE(); }
-
- static const int StackAlignment = 16;
- static const int StackShadowSpace = 0;
- static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
- static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {}
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0)
- as->add32(TrustedImm32(frameSize), StackPointerRegister);
- as->pop(FramePointerRegister);
- }
-
-#if OS(WINDOWS) || OS(QNX) || \
- ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__)))
-
- static const int gotRegister = JSC::X86Registers::ebx;
- static int savedGOTRegisterSlotOnStack() {
- static int ebxIdx = -1;
- if (ebxIdx == -1) {
- int calleeSaves = 0;
- const auto infos = getRegisterInfo();
- for (const RegisterInfo &info : infos) {
- if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) {
- ebxIdx = calleeSaves;
- break;
- } else if (info.isCalleeSaved()) {
- ++calleeSaves;
- }
- }
- Q_ASSERT(ebxIdx >= 0);
- ebxIdx += 1;
- }
- return ebxIdx * -int(sizeof(void*));
- }
-#else
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-#endif
-};
-#endif // x86
-
-#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX) || defined(Q_OS_IOS))
-template <>
-class TargetPlatform<JSC::MacroAssemblerX86_64, NoOperatingSystemSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerX86_64;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
-
- enum { RegAllocIsSupported = 1 };
-
- static const RegisterID FramePointerRegister = JSC::X86Registers::ebp;
- static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
- static const RegisterID LocalsRegister = JSC::X86Registers::r12;
- static const RegisterID EngineRegister = JSC::X86Registers::r14;
- static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
- static const RegisterID ScratchRegister = JSC::X86Registers::r10;
- static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13;
- static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
- static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- // r11 is used as scratch register by the macro assembler
- << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
- static const int RegisterSize = 8;
-
- static const int RegisterArgumentCount = 6;
- static RegisterID registerForArgument(int index)
- {
- static RegisterID regs[RegisterArgumentCount] = {
- JSC::X86Registers::edi,
- JSC::X86Registers::esi,
- JSC::X86Registers::edx,
- JSC::X86Registers::ecx,
- JSC::X86Registers::r8,
- JSC::X86Registers::r9
- };
- Q_ASSERT(index >= 0 && index < RegisterArgumentCount);
- return regs[index];
- };
-
- static const int StackAlignment = 16;
- static const int StackShadowSpace = 0;
- static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
- static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as)
- {
- as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister);
- }
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0)
- as->add64(TrustedImm32(frameSize), StackPointerRegister);
- as->pop(FramePointerRegister);
- }
-
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-};
-#endif // Linux/MacOS on x86_64
-
-#if CPU(X86_64) && OS(WINDOWS)
-template <>
-class TargetPlatform<JSC::MacroAssemblerX86_64, WindowsSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerX86_64;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
-
- enum { RegAllocIsSupported = 1 };
-
- static const RegisterID FramePointerRegister = JSC::X86Registers::ebp;
- static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
- static const RegisterID LocalsRegister = JSC::X86Registers::r12;
- static const RegisterID EngineRegister = JSC::X86Registers::r14;
- static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
- static const RegisterID ScratchRegister = JSC::X86Registers::r10;
- static const RegisterID DoubleMaskRegister = JSC::X86Registers::r13;
- static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
- static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- // r11 is used as scratch register by the macro assembler
- << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
- static const int RegisterSize = 8;
-
- static const int RegisterArgumentCount = 4;
- static RegisterID registerForArgument(int index)
- {
- static RegisterID regs[RegisterArgumentCount] = {
- JSC::X86Registers::ecx,
- JSC::X86Registers::edx,
- JSC::X86Registers::r8,
- JSC::X86Registers::r9
- };
- Q_ASSERT(index >= 0 && index < RegisterArgumentCount);
- return regs[index];
- }
-
- static const int StackAlignment = 16;
- static const int StackShadowSpace = 32;
- static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU.
- static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); }
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as)
- {
- as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister);
- }
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0)
- as->add64(TrustedImm32(frameSize), StackPointerRegister);
- as->pop(FramePointerRegister);
- }
-
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-};
-#endif // Windows on x86_64
-
-#if CPU(ARM) || defined(V4_BOOTSTRAP)
-template <>
-class TargetPlatform<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerARMv7;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
-
- enum { RegAllocIsSupported = 1 };
-
- // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are:
- // - The GNU/Linux ABI defines it as an additional callee-saved variable register (v6).
- // - iOS (for which we cannot JIT, but still...) defines it as having a special use, so we do
- // not touch it, nor use it.
- // - Any other platform has not been verified, so we conservatively assume we cannot use it.
-#if OS(LINUX)
-#define CAN_USE_R9
-#endif
-
- // There are two designated frame-pointer registers on ARM, depending on which instruction set
- // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants
- // accordingly, and assign the locals-register to the "other" register.
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
- static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
- static const RegisterID LocalsRegister = JSC::ARMRegisters::r11;
-#else // Thumbs down
- static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11;
- static const RegisterID LocalsRegister = JSC::ARMRegisters::r7;
-#endif
- static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13;
- static const RegisterID ScratchRegister = JSC::ARMRegisters::r5;
- static const RegisterID EngineRegister = JSC::ARMRegisters::r10;
- static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0;
- static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0;
- static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1;
- static const RegisterID LowReturnValueRegister = JSC::ARMRegisters::r0;
- static const RegisterID HighReturnValueRegister = JSC::ARMRegisters::r1;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined)
- << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::r3, QStringLiteral("r3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::r4, QStringLiteral("r4"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::r5, QStringLiteral("r5"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::ARMRegisters::r6, QStringLiteral("r6"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
-#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP)
- << RI(JSC::ARMRegisters::r7, QStringLiteral("r7"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
-#endif
- << RI(JSC::ARMRegisters::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
-#ifdef CAN_USE_R9
- << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
-#endif
- << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
- << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
-#endif
- << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d3, QStringLiteral("d3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d4, QStringLiteral("d4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d5, QStringLiteral("d5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d6, QStringLiteral("d6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d8, QStringLiteral("d8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d9, QStringLiteral("d9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d10, QStringLiteral("d10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d11, QStringLiteral("d11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d12, QStringLiteral("d12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d13, QStringLiteral("d13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d14, QStringLiteral("d14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARMRegisters::d15, QStringLiteral("d15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
- static const int RegisterSize = 4;
-
- static const int RegisterArgumentCount = 4;
- static RegisterID registerForArgument(int index)
- {
- static RegisterID regs[RegisterArgumentCount] = {
- JSC::ARMRegisters::r0,
- JSC::ARMRegisters::r1,
- JSC::ARMRegisters::r2,
- JSC::ARMRegisters::r3
- };
-
- Q_ASSERT(index >= 0 && index < RegisterArgumentCount);
- return regs[index];
- };
-
- static const int StackAlignment = 8; // Per AAPCS
- static const int StackShadowSpace = 0;
- static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below.
-
- static void platformEnterStandardStackFrame(PlatformAssembler *as)
- {
- as->push(JSC::ARMRegisters::lr);
- as->push(FramePointerRegister);
- }
-
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {}
-
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0) {
- // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
- // work well for large immediates.
- as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3);
- as->add32(JSC::ARMRegisters::r3, StackPointerRegister);
- }
- as->pop(FramePointerRegister);
- as->pop(JSC::ARMRegisters::lr);
- }
-
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-};
-#endif // ARM (32 bit)
-
-#if CPU(ARM64) || defined(V4_BOOTSTRAP)
-template <>
-class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerARM64;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
-
- enum { RegAllocIsSupported = 1 };
-
- static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp;
- static const RegisterID LocalsRegister = JSC::ARM64Registers::x28;
- static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp;
- static const RegisterID ScratchRegister = JSC::ARM64Registers::x9;
- static const RegisterID EngineRegister = JSC::ARM64Registers::x27;
- static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0;
- static const RegisterID DoubleMaskRegister = JSC::ARM64Registers::x26;
- static const FPRegisterID FPGpr0 = JSC::ARM64Registers::q0;
- static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- << RI(JSC::ARM64Registers::x0, QStringLiteral("x0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined)
- << RI(JSC::ARM64Registers::x1, QStringLiteral("x1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x2, QStringLiteral("x2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x3, QStringLiteral("x3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x4, QStringLiteral("x4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x5, QStringLiteral("x5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x6, QStringLiteral("x6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x7, QStringLiteral("x7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x8, QStringLiteral("x8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined)
- << RI(JSC::ARM64Registers::x9, QStringLiteral("x9"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::ARM64Registers::x10, QStringLiteral("x10"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x11, QStringLiteral("x11"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x12, QStringLiteral("x12"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x13, QStringLiteral("x13"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x14, QStringLiteral("x14"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x15, QStringLiteral("x15"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x19, QStringLiteral("x19"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x20, QStringLiteral("x20"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x21, QStringLiteral("x21"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x22, QStringLiteral("x22"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x23, QStringLiteral("x23"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x24, QStringLiteral("x24"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x25, QStringLiteral("x25"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::x26, QStringLiteral("x26"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::ARM64Registers::x27, QStringLiteral("x27"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::ARM64Registers::x28, QStringLiteral("x28"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
-
- << RI(JSC::ARM64Registers::q2, QStringLiteral("q2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q3, QStringLiteral("q3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q4, QStringLiteral("q4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q5, QStringLiteral("q5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q6, QStringLiteral("q6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q8, QStringLiteral("q8"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q9, QStringLiteral("q9"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q10, QStringLiteral("q10"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q11, QStringLiteral("q11"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q12, QStringLiteral("q12"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q13, QStringLiteral("q13"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q14, QStringLiteral("q14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q15, QStringLiteral("q15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q16, QStringLiteral("q16"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q17, QStringLiteral("q17"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q18, QStringLiteral("q18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q19, QStringLiteral("q19"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q20, QStringLiteral("q20"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q21, QStringLiteral("q21"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q22, QStringLiteral("q22"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q23, QStringLiteral("q23"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q24, QStringLiteral("q24"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q25, QStringLiteral("q25"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q26, QStringLiteral("q26"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q27, QStringLiteral("q27"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q28, QStringLiteral("q28"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q29, QStringLiteral("q29"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q30, QStringLiteral("q30"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::ARM64Registers::q31, QStringLiteral("q31"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
- static const int RegisterSize = 8;
-
- static const int RegisterArgumentCount = 8;
- static RegisterID registerForArgument(int index)
- {
- static RegisterID regs[RegisterArgumentCount] = {
- JSC::ARM64Registers::x0,
- JSC::ARM64Registers::x1,
- JSC::ARM64Registers::x2,
- JSC::ARM64Registers::x3,
- JSC::ARM64Registers::x4,
- JSC::ARM64Registers::x5,
- JSC::ARM64Registers::x6,
- JSC::ARM64Registers::x7
- };
-
- Q_ASSERT(index >= 0 && index < RegisterArgumentCount);
- return regs[index];
- };
-
- static const int StackAlignment = 16;
- static const int StackShadowSpace = 0;
- static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below.
-
- static void platformEnterStandardStackFrame(PlatformAssembler *as)
- {
- as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr);
- }
-
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *as)
- {
- as->move(PlatformAssembler::TrustedImm64(QV4::Value::NaNEncodeMask), DoubleMaskRegister);
- }
-
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0)
- as->add64(TrustedImm32(frameSize), StackPointerRegister);
- as->popPair(FramePointerRegister, JSC::ARM64Registers::lr);
- }
-
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-};
-#endif // ARM64
-
-#if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX)
-template <>
-class TargetPlatform<JSC::MacroAssemblerMIPS, NoOperatingSystemSpecialization>
-{
-public:
- using PlatformAssembler = JSC::MacroAssemblerMIPS;
- using RegisterID = PlatformAssembler::RegisterID;
- using FPRegisterID = PlatformAssembler::FPRegisterID;
- using TrustedImm32 = PlatformAssembler::TrustedImm32;
- enum { RegAllocIsSupported = 1 };
-
- static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp;
- static const RegisterID StackPointerRegister = JSC::MIPSRegisters::sp;
- static const RegisterID LocalsRegister = JSC::MIPSRegisters::s0;
- static const RegisterID EngineRegister = JSC::MIPSRegisters::s1;
- static const RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0;
- static const RegisterID ScratchRegister = JSC::MIPSRegisters::s2;
- static const FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0;
- static const FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2;
- static const RegisterID LowReturnValueRegister = JSC::MIPSRegisters::v0;
- static const RegisterID HighReturnValueRegister = JSC::MIPSRegisters::v1;
-
- static RegisterInformation getRegisterInfo()
- {
- typedef RegisterInfo RI;
- static RegisterInformation info = RegisterInformation()
- // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS.
- << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::t6, QStringLiteral("t6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::t7, QStringLiteral("t7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::t8, QStringLiteral("t8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::s0, QStringLiteral("s0"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::MIPSRegisters::s1, QStringLiteral("s1"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::MIPSRegisters::s2, QStringLiteral("s2"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined)
- << RI(JSC::MIPSRegisters::s3, QStringLiteral("s3"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f4, QStringLiteral("f4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f6, QStringLiteral("f6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f8, QStringLiteral("f8"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f10, QStringLiteral("f10"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f18, QStringLiteral("f18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f20, QStringLiteral("f20"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f22, QStringLiteral("f22"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f24, QStringLiteral("f24"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc)
- ;
- return info;
- }
-
-#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
- static const int RegisterSize = 4;
-
- static const int RegisterArgumentCount = 4;
- static RegisterID registerForArgument(int index)
- {
- static RegisterID regs[RegisterArgumentCount] = {
- JSC::MIPSRegisters::a0,
- JSC::MIPSRegisters::a1,
- JSC::MIPSRegisters::a2,
- JSC::MIPSRegisters::a3
- };
-
- Q_ASSERT(index >= 0 && index < RegisterArgumentCount);
- return regs[index];
- };
-
- static const int StackAlignment = 8;
- static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers.
- static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below.
-
- static void platformEnterStandardStackFrame(PlatformAssembler *as)
- {
- as->push(JSC::MIPSRegisters::ra);
- as->push(FramePointerRegister);
- }
-
- static void platformFinishEnteringStandardStackFrame(PlatformAssembler *) {}
-
- static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize)
- {
- if (frameSize > 0)
- as->add32(TrustedImm32(frameSize), StackPointerRegister);
- as->pop(FramePointerRegister);
- as->pop(JSC::MIPSRegisters::ra);
- }
-
-
- static const int gotRegister = -1;
- static int savedGOTRegisterSlotOnStack() { return -1; }
-};
-#endif // Linux on MIPS (32 bit)
-
-} // JIT namespace
-} // QV4 namespace
-
-QT_END_NAMESPACE
-
-#endif // ENABLE(ASSEMBLER)
-
-#endif // QV4TARGETPLATFORM_P_H
diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp
deleted file mode 100644
index 78546e1509..0000000000
--- a/src/qml/jit/qv4unop.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <qv4unop_p.h>
-#include <qv4assembler_p.h>
-
-#if ENABLE(ASSEMBLER)
-
-using namespace QV4;
-using namespace JIT;
-
-#define stringIfyx(s) #s
-#define stringIfy(s) stringIfyx(s)
-#define setOp(operation) \
- do { \
- call = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); name = "Runtime::" stringIfy(operation); \
- needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \
- } while (0)
-
-template <typename JITAssembler>
-void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target)
-{
- bool needsExceptionCheck;
- typename JITAssembler::RuntimeCall call;
- const char *name = 0;
- switch (op) {
- case IR::OpNot:
- generateNot(source, target);
- return;
- case IR::OpUMinus:
- generateUMinus(source, target);
- return;
- case IR::OpUPlus: setOp(uPlus); break;
- case IR::OpCompl:
- generateCompl(source, target);
- return;
- case IR::OpIncrement: setOp(increment); break;
- case IR::OpDecrement: setOp(decrement); break;
- default:
- Q_UNREACHABLE();
- } // switch
-
- Q_ASSERT(call.isValid());
- _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *targetTemp = target->asTemp();
-
- if (IR::Const *c = source->asConst()) {
- if (c->value == 0 && source->type == IR::SInt32Type) {
- // special case: minus integer 0 is 0, which is not what JS expects it to be, so always
- // do a runtime call
- generateRuntimeCall(_as, target, uMinus, PointerToValue(source));
- return;
- }
- }
- if (source->type == IR::SInt32Type) {
- typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
- tReg = (typename JITAssembler::RegisterID) targetTemp->index;
- typename JITAssembler::RegisterID sReg = _as->toInt32Register(source, tReg);
- _as->move(sReg, tReg);
- _as->neg32(tReg);
- if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- _as->storeInt32(tReg, target);
- return;
- }
-
- generateRuntimeCall(_as, target, uMinus, PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *targetTemp = target->asTemp();
- if (source->type == IR::BoolType) {
- typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
- tReg = (typename JITAssembler::RegisterID) targetTemp->index;
- _as->xor32(TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg);
- if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- _as->storeBool(tReg, target);
- return;
- } else if (source->type == IR::SInt32Type) {
- typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
- tReg = (typename JITAssembler::RegisterID) targetTemp->index;
- _as->compare32(RelationalCondition::Equal,
- _as->toInt32Register(source, JITAssembler::ScratchRegister), TrustedImm32(0),
- tReg);
- if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- _as->storeBool(tReg, target);
- return;
- } else if (source->type == IR::DoubleType) {
- // ###
- }
- // ## generic implementation testing for int/bool
-
- generateRuntimeCall(_as, target, uNot, PointerToValue(source));
-}
-
-template <typename JITAssembler>
-void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target)
-{
- IR::Temp *targetTemp = target->asTemp();
- if (source->type == IR::SInt32Type) {
- typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister;
- if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
- tReg = (typename JITAssembler::RegisterID) targetTemp->index;
- _as->xor32(TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg);
- if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- _as->storeInt32(tReg, target);
- return;
- }
- generateRuntimeCall(_as, target, complement, PointerToValue(source));
-}
-
-template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>;
-#if defined(V4_BOOTSTRAP)
-#if !CPU(ARM_THUMB2)
-template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>;
-#endif
-#if !CPU(ARM64)
-template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>;
-#endif
-#endif
-
-#endif
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index d5b8b295a7..bca4057fbe 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -443,15 +443,14 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
{
QV4::ExecutionEngine *v4 = d->m_v4Engine;
QV4::Scope scope(v4);
- QV4::ExecutionContextSaver saver(scope);
-
- QV4::ExecutionContext *ctx = v4->currentContext;
- if (ctx->d() != v4->rootContext()->d())
- ctx = v4->pushGlobalContext();
QV4::ScopedValue result(scope);
- QV4::Script script(ctx, program, fileName, lineNumber);
- script.strictMode = ctx->d()->strictMode;
+ QV4::Script script(v4->rootContext(), QV4::Compiler::EvalCode, program, fileName, lineNumber);
+ script.strictMode = false;
+ if (v4->currentStackFrame)
+ script.strictMode = v4->currentStackFrame->v4Function->isStrict();
+ else if (v4->globalCode)
+ script.strictMode = v4->globalCode->isStrict();
script.inheritContext = true;
script.parse();
if (!scope.engine->hasException)
@@ -459,7 +458,9 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
if (scope.engine->hasException)
result = v4->catchException();
- return QJSValue(v4, result->asReturnedValue());
+ QJSValue retval(v4, result->asReturnedValue());
+
+ return retval;
}
/*!
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 953e5d3ef6..c1a135c835 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -53,7 +53,7 @@
#include "qv4errorobject_p.h"
#include "private/qv8engine_p.h"
#include <private/qv4mm_p.h>
-#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
/*!
@@ -664,21 +664,21 @@ QJSValue QJSValue::call(const QJSValueList &args)
Q_ASSERT(engine);
Scope scope(engine);
- ScopedCallData callData(scope, args.length());
- callData->thisObject = engine->globalObject;
+ JSCallData jsCallData(scope, args.length());
+ *jsCallData->thisObject = engine->globalObject;
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
return QJSValue();
}
- callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- f->call(scope, callData);
+ ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
- scope.result = engine->catchException();
+ result = engine->catchException();
- return QJSValue(engine, scope.result.asReturnedValue());
+ return QJSValue(engine, result->asReturnedValue());
}
/*!
@@ -720,21 +720,21 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
return QJSValue();
}
- ScopedCallData callData(scope, args.size());
- callData->thisObject = QJSValuePrivate::convertedToValue(engine, instance);
+ JSCallData jsCallData(scope, args.size());
+ *jsCallData->thisObject = QJSValuePrivate::convertedToValue(engine, instance);
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
return QJSValue();
}
- callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- f->call(scope, callData);
+ ScopedValue result(scope, f->call(jsCallData));
if (engine->hasException)
- scope.result = engine->catchException();
+ result = engine->catchException();
- return QJSValue(engine, scope.result.asReturnedValue());
+ return QJSValue(engine, result->asReturnedValue());
}
/*!
@@ -769,20 +769,20 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
Q_ASSERT(engine);
Scope scope(engine);
- ScopedCallData callData(scope, args.size());
+ JSCallData jsCallData(scope, args.size());
for (int i = 0; i < args.size(); ++i) {
if (!QJSValuePrivate::checkEngine(engine, args.at(i))) {
qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine");
return QJSValue();
}
- callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
+ jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- f->construct(scope, callData);
+ ScopedValue result(scope, f->callAsConstructor(jsCallData));
if (engine->hasException)
- scope.result = engine->catchException();
+ result = engine->catchException();
- return QJSValue(engine, scope.result.asReturnedValue());
+ return QJSValue(engine, result->asReturnedValue());
}
#ifdef QT_DEPRECATED
@@ -1173,10 +1173,7 @@ bool QJSValue::deleteProperty(const QString &name)
return false;
ScopedString s(scope, engine->newString(name));
- bool b = o->deleteProperty(s);
- if (engine->hasException)
- engine->catchException();
- return b;
+ return o->deleteProperty(s);
}
/*!
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index ebdb3d6d03..519c87d0c4 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -33,6 +33,7 @@ SOURCES += \
$$PWD/qv4variantobject.cpp \
$$PWD/qv4objectiterator.cpp \
$$PWD/qv4regexp.cpp \
+ $$PWD/qv4runtimecodegen.cpp \
$$PWD/qv4serialize.cpp \
$$PWD/qv4script.cpp \
$$PWD/qv4sequenceobject.cpp \
@@ -58,6 +59,7 @@ HEADERS += \
$$PWD/qv4identifiertable_p.h \
$$PWD/qv4managed_p.h \
$$PWD/qv4internalclass_p.h \
+ $$PWD/qv4jscall_p.h \
$$PWD/qv4sparsearray_p.h \
$$PWD/qv4arraydata_p.h \
$$PWD/qv4arrayobject_p.h \
@@ -76,6 +78,7 @@ HEADERS += \
$$PWD/qv4objectproto_p.h \
$$PWD/qv4qmlcontext_p.h \
$$PWD/qv4regexpobject_p.h \
+ $$PWD/qv4runtimecodegen_p.h \
$$PWD/qv4stringobject_p.h \
$$PWD/qv4variantobject_p.h \
$$PWD/qv4property_p.h \
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 0905c2828a..075e7afd8a 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -41,40 +41,53 @@
#include <qv4scopedvalue_p.h>
#include <qv4string_p.h>
#include <qv4function_p.h>
+#include <qv4jscall_p.h>
using namespace QV4;
DEFINE_OBJECT_VTABLE(ArgumentsObject);
+DEFINE_OBJECT_VTABLE(StrictArgumentsObject);
-void Heap::ArgumentsObject::init(QV4::CallContext *context)
+void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame)
{
ExecutionEngine *v4 = internalClass->engine;
+ int nFormals = frame->v4Function->nFormals;
+ QV4::CallContext *context = static_cast<QV4::CallContext *>(frame->context());
+
Object::init();
fullyCreated = false;
+ this->nFormals = nFormals;
this->context.set(v4, context->d());
Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
+ setProperty(v4, CalleePropertyIndex, context->d()->function);
+ Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()));
+ setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc()));
+}
+
+void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
+{
+ Q_ASSERT(vtable() == QV4::StrictArgumentsObject::staticVTable());
+ ExecutionEngine *v4 = internalClass->engine;
+
+ Object::init();
+
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
+ Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller()));
+ setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
+ setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
+ setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
+ setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
+
Scope scope(v4);
- Scoped<QV4::ArgumentsObject> args(scope, this);
-
- if (context->d()->strictMode) {
- Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee()));
- Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller()));
- args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
- args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- args->setProperty(CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
-
- args->arrayReserve(context->argc());
- args->arrayPut(0, context->args(), context->argc());
- args->d()->fullyCreated = true;
- } else {
- Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee()));
- args->setProperty(CalleePropertyIndex, context->d()->function);
- }
+ Scoped<QV4::StrictArgumentsObject> args(scope, this);
+ args->arrayReserve(frame->originalArgumentsCount);
+ args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount);
+
Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()));
- args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc));
+ setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount));
}
void ArgumentsObject::fullyCreate()
@@ -84,8 +97,8 @@ void ArgumentsObject::fullyCreate()
Scope scope(engine());
- uint argCount = context()->callData->argc;
- uint numAccessors = qMin(context()->formalParameterCount(), argCount);
+ int argCount = context()->argc();
+ uint numAccessors = qMin(d()->nFormals, argCount);
ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true);
scope.engine->requireArgumentsAccessors(numAccessors);
@@ -93,12 +106,12 @@ void ArgumentsObject::fullyCreate()
if (numAccessors) {
d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors));
for (uint i = 0; i < numAccessors; ++i) {
- d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]);
+ d()->mappedArguments->values.set(scope.engine, i, context()->args()[i]);
arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor);
}
}
- arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors);
- for (uint i = numAccessors; i < argCount; ++i)
+ arrayPut(numAccessors, context()->args() + numAccessors, argCount - numAccessors);
+ for (int i = int(numAccessors); i < argCount; ++i)
setArrayAttributes(i, Attr_Data);
d()->fullyCreated = true;
@@ -111,7 +124,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
Scope scope(engine);
ScopedProperty map(scope);
PropertyAttributes mapAttrs;
- uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->callData->argc));
+ uint numAccessors = qMin(d()->nFormals, context()->argc());
bool isMapped = false;
if (arrayData() && index < numAccessors &&
arrayData()->attributes(index).isAccessor() &&
@@ -127,18 +140,18 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
arrayIndex.set(scope.engine, d()->mappedArguments->values[index]);
}
- bool strict = engine->current->strictMode;
- engine->current->strictMode = false;
bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs);
- engine->current->strictMode = strict;
+ if (!result) {
+ return false;
+ }
if (isMapped && attrs.isData()) {
Q_ASSERT(arrayData());
ScopedFunctionObject setter(scope, map->setter());
- ScopedCallData callData(scope, 1);
- callData->thisObject = this->asReturnedValue();
- callData->args[0] = desc->value;
- setter->call(scope, callData);
+ JSCallData jsCallData(scope, 1);
+ *jsCallData->thisObject = this->asReturnedValue();
+ jsCallData->args[0] = desc->value;
+ setter->call(jsCallData);
if (attrs.isWritable()) {
setArrayAttributes(index, mapAttrs);
@@ -146,8 +159,6 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
}
}
- if (engine->current->strictMode && !result)
- return engine->throwTypeError();
return result;
}
@@ -157,10 +168,10 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha
if (args->fullyCreated())
return Object::getIndexed(m, index, hasProperty);
- if (index < static_cast<uint>(args->context()->callData->argc)) {
+ if (index < static_cast<uint>(args->context()->argc())) {
if (hasProperty)
*hasProperty = true;
- return args->context()->callData->args[index].asReturnedValue();
+ return args->context()->args()[index].asReturnedValue();
}
if (hasProperty)
*hasProperty = false;
@@ -170,13 +181,13 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha
bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value)
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc))
+ if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc()))
args->fullyCreate();
if (args->fullyCreated())
return Object::putIndexed(m, index, value);
- args->context()->callData->args[index] = value;
+ args->context()->setArg(index, value);
return true;
}
@@ -194,8 +205,8 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
if (args->fullyCreated())
return Object::queryIndexed(m, index);
- uint numAccessors = qMin((int)args->context()->formalParameterCount(), args->context()->callData->argc);
- uint argCount = args->context()->callData->argc;
+ uint numAccessors = qMin(args->d()->nFormals, args->context()->argc());
+ uint argCount = args->context()->argc();
if (index >= argCount)
return PropertyAttributes();
if (index >= numAccessors)
@@ -205,35 +216,33 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction);
-void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData)
+ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int)
{
- ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine();
- Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter));
- Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>());
- if (!o) {
- scope.result = v4->throwTypeError();
- return;
- }
+ ExecutionEngine *v4 = getter->engine();
+ Scope scope(v4);
+ const ArgumentsGetterFunction *g = static_cast<const ArgumentsGetterFunction *>(getter);
+ Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>());
+ if (!o)
+ return v4->throwTypeError();
- Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->callData->argc));
- scope.result = o->context()->callData->args[g->index()];
+ Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->argc()));
+ return o->context()->args()[g->index()].asReturnedValue();
}
DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction);
-void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData)
+ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc)
{
- ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine();
- Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter));
- Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>());
- if (!o) {
- scope.result = v4->throwTypeError();
- return;
- }
+ ExecutionEngine *v4 = setter->engine();
+ Scope scope(v4);
+ const ArgumentsSetterFunction *s = static_cast<const ArgumentsSetterFunction *>(setter);
+ Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>());
+ if (!o)
+ return v4->throwTypeError();
- Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->callData->argc));
- o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined();
- scope.result = Encode::undefined();
+ Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->argc()));
+ o->context()->setArg(s->index(), argc ? argv[0] : Primitive::undefinedValue());
+ return Encode::undefined();
}
uint ArgumentsObject::getLength(const Managed *m)
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index 46e1f884e8..ac281f555a 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -63,7 +63,7 @@ namespace Heap {
Member(class, NoMark, uint, index)
DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) {
- DECLARE_MARK_TABLE(ArgumentsGetterFunction);
+ DECLARE_MARKOBJECTS(ArgumentsGetterFunction);
inline void init(QV4::ExecutionContext *scope, uint index);
};
@@ -71,23 +71,34 @@ DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) {
Member(class, NoMark, uint, index)
DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) {
- DECLARE_MARK_TABLE(ArgumentsSetterFunction);
+ DECLARE_MARKOBJECTS(ArgumentsSetterFunction);
inline void init(QV4::ExecutionContext *scope, uint index);
};
#define ArgumentsObjectMembers(class, Member) \
Member(class, Pointer, CallContext *, context) \
Member(class, Pointer, MemberData *, mappedArguments) \
- Member(class, NoMark, bool, fullyCreated)
+ Member(class, NoMark, bool, fullyCreated) \
+ Member(class, NoMark, int, nFormals)
DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
- DECLARE_MARK_TABLE(ArgumentsObject);
+ DECLARE_MARKOBJECTS(ArgumentsObject);
+ enum {
+ LengthPropertyIndex = 0,
+ CalleePropertyIndex = 1
+ };
+ void init(CppStackFrame *frame);
+};
+
+#define StrictArgumentsObjectMembers(class, Member)
+
+DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) {
enum {
LengthPropertyIndex = 0,
CalleePropertyIndex = 1,
CallerPropertyIndex = 3
};
- void init(QV4::CallContext *context);
+ void init(CppStackFrame *frame);
};
}
@@ -97,7 +108,7 @@ struct ArgumentsGetterFunction: FunctionObject
V4_OBJECT2(ArgumentsGetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static void call(const Managed *that, Scope &scope, CallData *d);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
inline void
@@ -112,7 +123,7 @@ struct ArgumentsSetterFunction: FunctionObject
V4_OBJECT2(ArgumentsSetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
inline void
@@ -131,8 +142,7 @@ struct ArgumentsObject: Object {
bool fullyCreated() const { return d()->fullyCreated; }
static bool isNonStrictArgumentsObject(Managed *m) {
- return m->d()->vtable()->type == Type_ArgumentsObject &&
- !static_cast<ArgumentsObject *>(m)->context()->strictMode;
+ return m->d()->vtable() == staticVTable();
}
bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs);
@@ -146,6 +156,11 @@ struct ArgumentsObject: Object {
};
+struct StrictArgumentsObject : Object {
+ V4_OBJECT2(StrictArgumentsObject, Object)
+ Q_MANAGED_TYPE(ArgumentsObject)
+};
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index ffe9aa846f..f7b9e8acef 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -40,6 +40,7 @@
#include "qv4typedarray_p.h"
#include "qv4dataview_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
using namespace QV4;
@@ -51,49 +52,42 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer"));
}
-void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
+ ExecutionEngine *v4 = f->engine();
+ Scope scope(v4);
- ScopedValue l(scope, callData->argument(0));
+ ScopedValue l(scope, argc ? argv[0] : Primitive::undefinedValue());
double dl = l->toInteger();
- if (v4->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (v4->hasException)
+ return Encode::undefined();
uint len = (uint)qBound(0., dl, (double)UINT_MAX);
- if (len != dl) {
- scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
- return;
- }
+ if (len != dl)
+ return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len));
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- } else {
- scope.result = a->asReturnedValue();
- }
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ return a->asReturnedValue();
}
-void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
-void ArrayBufferCtor::method_isView(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- QV4::Scoped<TypedArray> a(scope, callData->argument(0));
- if (!!a) {
- scope.result = Encode(true);
- return;
- }
- QV4::Scoped<DataView> v(scope, callData->argument(0));
- if (!!v) {
- scope.result = Encode(true);
- return;
- }
- scope.result = Encode(false);
+ if (argc < 1)
+ return Encode(false);
+
+ if (argv[0].as<TypedArray>() ||
+ argv[0].as<DataView>())
+ return Encode(true);
+
+ return Encode(false);
}
@@ -163,48 +157,51 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
}
-void ArrayBufferPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<ArrayBuffer> v(scope, callData->thisObject);
- if (!v)
- THROW_TYPE_ERROR();
+ const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
+ if (!a)
+ return b->engine()->throwTypeError();
- scope.result = Encode(v->d()->data->size);
+ return Encode(a->d()->data->size);
}
-void ArrayBufferPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<ArrayBuffer> a(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
if (!a)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- double start = callData->argc > 0 ? callData->args[0].toInteger() : 0;
- double end = (callData->argc < 2 || callData->args[1].isUndefined()) ?
- a->d()->data->size : callData->args[1].toInteger();
- CHECK_EXCEPTION();
+ double start = argc > 0 ? argv[0].toInteger() : 0;
+ double end = (argc < 2 || argv[1].isUndefined()) ?
+ a->d()->data->size : argv[1].toInteger();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size);
double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size);
+ Scope scope(v4);
ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor()));
if (!constructor)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- ScopedCallData cData(scope, 1);
double newLen = qMax(final - first, 0.);
- cData->args[0] = QV4::Encode(newLen);
- constructor->construct(scope, cData);
- QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result);
+ ScopedValue argument(scope, QV4::Encode(newLen));
+ QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1));
if (!newBuffer || newBuffer->d()->data->size < (int)newLen)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen);
+ return Encode::undefined();
}
-void ArrayBufferPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<ArrayBuffer> a(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
if (!a)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(QString::fromUtf8(a->asByteArray()));
+ return Encode(v4->newString(QString::fromUtf8(a->asByteArray())));
}
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 4f7926d3dc..e236a23d1f 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -78,10 +78,10 @@ struct ArrayBufferCtor: FunctionObject
{
V4_OBJECT2(ArrayBufferCtor, FunctionObject)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
- static void method_isView(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
@@ -104,9 +104,9 @@ struct ArrayBufferPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index df9884d84a..390840fb1e 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -43,6 +43,7 @@
#include "qv4runtime_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
using namespace QV4;
@@ -52,7 +53,6 @@ const QV4::VTable QV4::ArrayData::static_vtbl = {
0,
0,
0,
- 0,
QV4::ArrayData::IsExecutionContext,
QV4::ArrayData::IsString,
QV4::ArrayData::IsObject,
@@ -63,7 +63,7 @@ const QV4::VTable QV4::ArrayData::static_vtbl = {
QV4::ArrayData::MyType,
"ArrayData",
Q_VTABLE_FUNCTION(QV4::ArrayData, destroy),
- 0,
+ ArrayData::Data::markObjects,
isEqualTo
};
@@ -111,6 +111,13 @@ static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value)
*target = v.asReturnedValue();
}
+void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack)
+{
+ ArrayData *a = static_cast<ArrayData *>(base);
+ a->values.mark(stack);
+}
+
+
void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAttributes)
{
Scope scope(o->engine());
@@ -161,6 +168,8 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
}
newData->setAlloc(alloc);
newData->setType(newType);
+ if (d)
+ newData->d()->needsMark = d->d()->needsMark;
newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->values.values + alloc) : 0);
o->setArrayData(newData);
@@ -183,6 +192,8 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
memcpy(newData->d()->values.values, d->d()->values.values + offset, sizeof(Value)*toCopy);
}
+ if (newType != Heap::ArrayData::Simple)
+ newData->d()->needsMark = true;
if (newType != Heap::ArrayData::Sparse)
return;
@@ -672,15 +683,14 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
return false;
if (v2.isUndefined() || v2.isEmpty())
return true;
- ScopedObject o(scope, m_comparefn);
+ ScopedFunctionObject o(scope, m_comparefn);
if (o) {
Scope scope(o->engine());
ScopedValue result(scope);
- ScopedCallData callData(scope, 2);
- callData->thisObject = Primitive::undefinedValue();
- callData->args[0] = v1;
- callData->args[1] = v2;
- result = QV4::Runtime::method_callValue(scope.engine, m_comparefn, callData);
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = v1;
+ jsCallData->args[1] = v2;
+ result = o->call(jsCallData);
return result->toNumber() < 0;
}
@@ -754,7 +764,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
if (!arrayData || !arrayData->length())
return;
- if (!(comparefn.isUndefined() || comparefn.as<Object>())) {
+ if (!comparefn.isUndefined() && !comparefn.isFunctionObject()) {
engine->throwTypeError();
return;
}
@@ -833,7 +843,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
}
- ArrayElementLessThan lessThan(engine, thisObject, comparefn);
+ ArrayElementLessThan lessThan(engine, thisObject, static_cast<const FunctionObject &>(comparefn));
Value *begin = thisObject->arrayData()->values.values;
sortHelper(begin, begin + len, *begin, lessThan);
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index e1de2e82e6..db9db5a220 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -91,7 +91,8 @@ struct ArrayVTable
namespace Heap {
#define ArrayDataMembers(class, Member) \
- Member(class, NoMark, uint, type) \
+ Member(class, NoMark, ushort, type) \
+ Member(class, NoMark, ushort, needsMark) \
Member(class, NoMark, uint, offset) \
Member(class, NoMark, PropertyAttributes *, attrs) \
Member(class, NoMark, ReturnedValue, freeList) \
@@ -99,7 +100,7 @@ namespace Heap {
Member(class, ValueArray, ValueArray, values)
DECLARE_HEAP_OBJECT(ArrayData, Base) {
- DECLARE_MARK_TABLE(ArrayData);
+ static void markObjects(Heap::Base *base, MarkStack *stack);
enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 };
@@ -144,9 +145,11 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
V4_ASSERT_IS_TRIVIAL(ArrayData)
struct SimpleArrayData : public ArrayData {
- uint mappedIndex(uint index) const { return (index + offset) % values.alloc; }
+ uint mappedIndex(uint index) const { index += offset; if (index >= values.alloc) index -= values.alloc; return index; }
const Value &data(uint index) const { return values[mappedIndex(index)]; }
void setData(EngineBase *e, uint index, Value newVal) {
+ if (newVal.isManaged())
+ needsMark = true;
values.set(e, mappedIndex(index), newVal);
}
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index a2c19e1f2d..bd019d3bcb 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -40,7 +40,7 @@
#include "qv4arrayobject_p.h"
#include "qv4sparsearray_p.h"
#include "qv4objectproto_p.h"
-#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
@@ -55,35 +55,34 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Array"));
}
-void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine();
+ ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine();
+ Scope scope(v4);
ScopedArrayObject a(scope, v4->newArrayObject());
uint len;
- if (callData->argc == 1 && callData->args[0].isNumber()) {
+ if (argc == 1 && argv[0].isNumber()) {
bool ok;
- len = callData->args[0].asArrayLength(&ok);
+ len = argv[0].asArrayLength(&ok);
- if (!ok) {
- scope.result = v4->throwRangeError(callData->args[0]);
- return;
- }
+ if (!ok)
+ return v4->throwRangeError(argv[0]);
if (len < 0x1000)
a->arrayReserve(len);
} else {
- len = callData->argc;
+ len = argc;
a->arrayReserve(len);
- a->arrayPut(0, callData->args, len);
+ a->arrayPut(0, argv, len);
}
a->setArrayLengthUnchecked(len);
- scope.result = a.asReturnedValue();
+ return a.asReturnedValue();
}
-void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -119,35 +118,35 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
}
-void ArrayPrototype::method_isArray(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- bool isArray = callData->argc && callData->args[0].as<ArrayObject>();
- scope.result = Encode(isArray);
+ bool isArray = argc && argv[0].as<ArrayObject>();
+ return Encode(isArray);
}
-void ArrayPrototype::method_toString(const BuiltinFunction *builtin, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->thisObject, ScopedObject::Convert);
- CHECK_EXCEPTION();
- ScopedString s(scope, scope.engine->newString(QStringLiteral("join")));
- ScopedFunctionObject f(scope, o->get(s));
- if (!!f) {
- ScopedCallData d(scope, 0);
- d->thisObject = callData->thisObject;
- f->call(scope, d);
- return;
- }
- ObjectPrototype::method_toString(builtin, scope, callData);
+ Scope scope(builtin);
+ ScopedObject that(scope, thisObject->toObject(scope.engine));
+ if (scope.hasException())
+ return QV4::Encode::undefined();
+
+ ScopedString string(scope, scope.engine->newString(QStringLiteral("join")));
+ ScopedFunctionObject f(scope, that->get(string));
+ if (f)
+ return f->call(that, argv, argc);
+ return ObjectPrototype::method_toString(builtin, that, argv, argc);
}
-void ArrayPrototype::method_toLocaleString(const BuiltinFunction *builtin, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
- return method_toString(builtin, scope, callData);
+ return method_toString(builtin, thisObject, argv, argc);
}
-void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc)
{
- ScopedObject thisObject(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject thisObject(scope, that->toObject(scope.engine));
if (!thisObject)
RETURN_UNDEFINED();
@@ -162,9 +161,9 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa
ScopedArrayObject elt(scope);
ScopedObject eltAsObj(scope);
ScopedValue entry(scope);
- for (int i = 0; i < callData->argc; ++i) {
- eltAsObj = callData->args[i];
- elt = callData->args[i];
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ eltAsObj = argv[i];
+ elt = argv[i];
if (elt) {
uint n = elt->getLength();
uint newLen = ArrayData::append(result, elt, n);
@@ -173,93 +172,94 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa
const uint startIndex = result->getLength();
for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
entry = eltAsObj->getIndexed(i);
+ // spec says not to throw if this fails
result->putIndexed(startIndex + i, entry);
}
} else {
- result->arraySet(result->getLength(), callData->args[i]);
+ result->arraySet(result->getLength(), argv[i]);
}
}
- scope.result = result;
+ return result.asReturnedValue();
}
-void ArrayPrototype::method_find(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv[0].isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
- ScopedValue v(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
for (uint k = 0; k < len; ++k) {
- v = instance->getIndexed(k);
+ arguments[0] = instance->getIndexed(k);
CHECK_EXCEPTION();
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ result = callback->call(that, arguments, 3);
CHECK_EXCEPTION();
- if (scope.result.toBoolean())
- RETURN_RESULT(v);
+ if (result->toBoolean())
+ return arguments[0].asReturnedValue();
}
RETURN_UNDEFINED();
}
-void ArrayPrototype::method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv[0].isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
- ScopedValue v(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
for (uint k = 0; k < len; ++k) {
- v = instance->getIndexed(k);
+ arguments[0] = instance->getIndexed(k);
CHECK_EXCEPTION();
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ result = callback->call(that, arguments, 3);
CHECK_EXCEPTION();
- if (scope.result.toBoolean())
- RETURN_RESULT(Encode(k));
+ if (result->toBoolean())
+ return Encode(k);
}
- RETURN_RESULT(Encode(-1));
+ return Encode(-1);
}
-void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedValue arg(scope, callData->argument(0));
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
- if (!instance) {
- scope.result = scope.engine->newString();
- return;
- }
+ if (!instance)
+ return Encode(scope.engine->newString());
+
+ ScopedValue arg(scope, argc ? argv[0] : Primitive::undefinedValue());
QString r4;
if (arg->isUndefined())
@@ -270,10 +270,8 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData
ScopedValue length(scope, instance->get(scope.engine->id_length()));
const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32();
- if (!r2) {
- scope.result = scope.engine->newString();
- return;
- }
+ if (!r2)
+ return Encode(scope.engine->newString());
QString R;
@@ -311,12 +309,13 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData
}
}
- scope.result = scope.engine->newString(R);
+ return Encode(scope.engine->newString(R));
}
-void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -331,19 +330,22 @@ void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData
ScopedValue result(scope, instance->getIndexed(len - 1));
CHECK_EXCEPTION();
- instance->deleteIndexedProperty(len - 1);
- CHECK_EXCEPTION();
+ if (!instance->deleteIndexedProperty(len - 1))
+ return scope.engine->throwTypeError();
if (instance->isArrayObject())
instance->setArrayLength(len - 1);
- else
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)));
- scope.result = result;
+ else {
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))))
+ return scope.engine->throwTypeError();
+ }
+ return result->asReturnedValue();
}
-void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -352,47 +354,52 @@ void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData
uint len = instance->getLength();
- if (len + callData->argc < len) {
- // ughh...
+ if (len + argc < len) {
+ // ughh... this goes beyond UINT_MAX
double l = len;
ScopedString s(scope);
- for (int i = 0; i < callData->argc; ++i) {
+ for (int i = 0, ei = argc; i < ei; ++i) {
s = Primitive::fromDouble(l + i).toString(scope.engine);
- instance->put(s, callData->args[i]);
+ if (!instance->put(s, argv[i]))
+ return scope.engine->throwTypeError();
}
- double newLen = l + callData->argc;
- if (!instance->isArrayObject())
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)));
- else {
+ double newLen = l + argc;
+ if (!instance->isArrayObject()) {
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))))
+ return scope.engine->throwTypeError();
+ } else {
ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow")));
- scope.result = scope.engine->throwRangeError(str);
- return;
+ return scope.engine->throwRangeError(str);
}
- scope.result = Encode(newLen);
- return;
+ return Encode(newLen);
}
- if (!callData->argc)
+ if (!argc)
;
else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) {
- instance->arrayData()->vtable()->putArray(instance, len, callData->args, callData->argc);
+ instance->arrayData()->vtable()->putArray(instance, len, argv, argc);
len = instance->arrayData()->length();
} else {
- for (int i = 0; i < callData->argc; ++i)
- instance->putIndexed(len + i, callData->args[i]);
- len += callData->argc;
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ if (!instance->putIndexed(len + i, argv[i]))
+ return scope.engine->throwTypeError();
+ }
+ len += argc;
}
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(len);
- else
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len)));
+ else {
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))))
+ return scope.engine->throwTypeError();
+ }
- scope.result = Encode(len);
+ return Encode(len);
}
-void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -407,22 +414,27 @@ void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallD
lval = instance->getIndexed(lo, &loExists);
hval = instance->getIndexed(hi, &hiExists);
CHECK_EXCEPTION();
+ bool ok;
if (hiExists)
- instance->putIndexed(lo, hval);
- else
- instance->deleteIndexedProperty(lo);
- CHECK_EXCEPTION();
- if (loExists)
- instance->putIndexed(hi, lval);
+ ok = instance->putIndexed(lo, hval);
else
- instance->deleteIndexedProperty(hi);
+ ok = instance->deleteIndexedProperty(lo);
+ if (ok) {
+ if (loExists)
+ ok = instance->putIndexed(hi, lval);
+ else
+ ok = instance->deleteIndexedProperty(hi);
+ }
+ if (!ok)
+ return scope.engine->throwTypeError();
}
- scope.result = instance;
+ return instance->asReturnedValue();
}
-void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -433,14 +445,16 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat
if (!len) {
if (!instance->isArrayObject())
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0)));
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))))
+ return scope.engine->throwTypeError();
RETURN_UNDEFINED();
}
+ ScopedValue result(scope);
if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) {
- scope.result = instance->arrayData()->vtable()->pop_front(instance);
+ result = instance->arrayData()->vtable()->pop_front(instance);
} else {
- scope.result = instance->getIndexed(0);
+ result = instance->getIndexed(0);
CHECK_EXCEPTION();
ScopedValue v(scope);
// do it the slow way
@@ -448,31 +462,40 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat
bool exists;
v = instance->getIndexed(k, &exists);
CHECK_EXCEPTION();
+ bool ok;
if (exists)
- instance->putIndexed(k - 1, v);
+ ok = instance->putIndexed(k - 1, v);
else
- instance->deleteIndexedProperty(k - 1);
- CHECK_EXCEPTION();
+ ok = instance->deleteIndexedProperty(k - 1);
+ if (!ok)
+ return scope.engine->throwTypeError();
}
- instance->deleteIndexedProperty(len - 1);
- CHECK_EXCEPTION();
+ bool ok = instance->deleteIndexedProperty(len - 1);
+ if (!ok)
+ return scope.engine->throwTypeError();
}
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(len - 1);
- else
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)));
+ else {
+ bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)));
+ if (!ok)
+ return scope.engine->throwTypeError();
+ }
+
+ return result->asReturnedValue();
}
-void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject o(scope, thisObject->toObject(scope.engine));
if (!o)
RETURN_UNDEFINED();
ScopedArrayObject result(scope, scope.engine->newArrayObject());
uint len = o->getLength();
- double s = ScopedValue(scope, callData->argument(0))->toInteger();
+ double s = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
uint start;
if (s < 0)
start = (uint)qMax(len + s, 0.);
@@ -481,8 +504,8 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat
else
start = (uint) s;
uint end = len;
- if (callData->argc > 1 && !callData->args[1].isUndefined()) {
- double e = callData->args[1].toInteger();
+ if (argc > 1 && !argv[1].isUndefined()) {
+ double e = argv[1].toInteger();
if (e < 0)
end = (uint)qMax(len + e, 0.);
else if (e > len)
@@ -501,25 +524,27 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat
result->arraySet(n, v);
++n;
}
- scope.result = result;
+ return result->asReturnedValue();
}
-void ArrayPrototype::method_sort(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedValue comparefn(scope, callData->argument(0));
+ ScopedValue comparefn(scope, argc ? argv[0] : Primitive::undefinedValue());
ArrayData::sort(scope.engine, instance, comparefn, len);
- scope.result = callData->thisObject;
+ return thisObject->asReturnedValue();
}
-void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -527,14 +552,14 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa
ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
- double rs = ScopedValue(scope, callData->argument(0))->toInteger();
+ double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
uint start;
if (rs < 0)
start = (uint) qMax(0., len + rs);
else
start = (uint) qMin(rs, (double)len);
- uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, callData->argument(1))->toInteger(), 0.), (double)(len - start));
+ uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start));
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
@@ -547,22 +572,24 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa
}
newArray->setArrayLengthUnchecked(deleteCount);
- uint itemCount = callData->argc < 2 ? 0 : callData->argc - 2;
+ uint itemCount = argc < 2 ? 0 : argc - 2;
if (itemCount < deleteCount) {
for (uint k = start; k < len - deleteCount; ++k) {
bool exists;
v = instance->getIndexed(k + deleteCount, &exists);
CHECK_EXCEPTION();
+ bool ok;
if (exists)
- instance->putIndexed(k + itemCount, v);
+ ok = instance->putIndexed(k + itemCount, v);
else
- instance->deleteIndexedProperty(k + itemCount);
- CHECK_EXCEPTION();
+ ok = instance->deleteIndexedProperty(k + itemCount);
+ if (!ok)
+ return scope.engine->throwTypeError();
}
for (uint k = len; k > len - deleteCount + itemCount; --k) {
- instance->deleteIndexedProperty(k - 1);
- CHECK_EXCEPTION();
+ if (!instance->deleteIndexedProperty(k - 1))
+ return scope.engine->throwTypeError();
}
} else if (itemCount > deleteCount) {
uint k = len - deleteCount;
@@ -570,31 +597,30 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa
bool exists;
v = instance->getIndexed(k + deleteCount - 1, &exists);
CHECK_EXCEPTION();
+ bool ok;
if (exists)
- instance->putIndexed(k + itemCount - 1, v);
+ ok = instance->putIndexed(k + itemCount - 1, v);
else
- instance->deleteIndexedProperty(k + itemCount - 1);
- CHECK_EXCEPTION();
+ ok = instance->deleteIndexedProperty(k + itemCount - 1);
+ if (!ok)
+ return scope.engine->throwTypeError();
--k;
}
}
- for (uint i = 0; i < itemCount; ++i) {
- instance->putIndexed(start + i, callData->args[i + 2]);
- CHECK_EXCEPTION();
- }
+ for (uint i = 0; i < itemCount; ++i)
+ instance->putIndexed(start + i, argv[i + 2]);
- bool wasStrict = scope.engine->current->strictMode;
- scope.engine->current->strictMode = true;
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)));
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))))
+ return scope.engine->throwTypeError();
- scope.result = newArray;
- scope.engine->current->strictMode = wasStrict;
+ return newArray->asReturnedValue();
}
-void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -605,52 +631,57 @@ void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallD
if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len &&
instance->arrayData()->type != Heap::ArrayData::Custom) {
- instance->arrayData()->vtable()->push_front(instance, callData->args, callData->argc);
+ instance->arrayData()->vtable()->push_front(instance, argv, argc);
} else {
ScopedValue v(scope);
for (uint k = len; k > 0; --k) {
bool exists;
v = instance->getIndexed(k - 1, &exists);
+ bool ok;
if (exists)
- instance->putIndexed(k + callData->argc - 1, v);
+ ok = instance->putIndexed(k + argc - 1, v);
else
- instance->deleteIndexedProperty(k + callData->argc - 1);
+ ok = instance->deleteIndexedProperty(k + argc - 1);
+ if (!ok)
+ return scope.engine->throwTypeError();
+ }
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ bool ok = instance->putIndexed(i, argv[i]);
+ if (!ok)
+ return scope.engine->throwTypeError();
}
- for (int i = 0; i < callData->argc; ++i)
- instance->putIndexed(i, callData->args[i]);
}
- uint newLen = len + callData->argc;
+ uint newLen = len + argc;
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(newLen);
- else
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)));
+ else {
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))))
+ return scope.engine->throwTypeError();
+ }
- scope.result = Encode(newLen);
+ return Encode(newLen);
}
-void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- if (!len) {
- scope.result = Encode(-1);
- return;
- }
+ if (!len)
+ return Encode(-1);
- ScopedValue searchValue(scope, callData->argument(0));
+ ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue());
uint fromIndex = 0;
- if (callData->argc >= 2) {
- double f = callData->args[1].toInteger();
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
CHECK_EXCEPTION();
- if (f >= len) {
- scope.result = Encode(-1);
- return;
- }
+ if (f >= len)
+ return Encode(-1);
if (f < 0)
f = qMax(len + f, 0.);
fromIndex = (uint) f;
@@ -661,13 +692,10 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD
for (uint k = fromIndex; k < len; ++k) {
bool exists;
v = instance->getIndexed(k, &exists);
- if (exists && RuntimeHelpers::strictEqual(v, searchValue)) {
- scope.result = Encode(k);
- return;
- }
+ if (exists && RuntimeHelpers::strictEqual(v, searchValue))
+ return Encode(k);
}
- scope.result = Encode(-1);
- return;
+ return Encode(-1);
}
ScopedValue value(scope);
@@ -679,14 +707,11 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD
bool exists;
value = instance->getIndexed(i, &exists);
CHECK_EXCEPTION();
- if (exists && RuntimeHelpers::strictEqual(value, searchValue)) {
- scope.result = Encode(i);
- return;
- }
+ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
+ return Encode(i);
}
} else if (!instance->arrayData()) {
- scope.result = Encode(-1);
- return;
+ return Encode(-1);
} else {
Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex);
Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>();
@@ -696,47 +721,42 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD
while (idx < len) {
value = sa->data(idx);
CHECK_EXCEPTION();
- if (RuntimeHelpers::strictEqual(value, searchValue)) {
- scope.result = Encode(idx);
- return;
- }
+ if (RuntimeHelpers::strictEqual(value, searchValue))
+ return Encode(idx);
++idx;
}
}
- scope.result = Encode(-1);
+ return Encode(-1);
}
-void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- if (!len) {
- scope.result = Encode(-1);
- return;
- }
+ if (!len)
+ return Encode(-1);
ScopedValue searchValue(scope);
uint fromIndex = len;
- if (callData->argc >= 1)
- searchValue = callData->argument(0);
+ if (argc >= 1)
+ searchValue = argv[0];
else
searchValue = Primitive::undefinedValue();
- if (callData->argc >= 2) {
- double f = callData->args[1].toInteger();
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
CHECK_EXCEPTION();
if (f > 0)
f = qMin(f, (double)(len - 1));
else if (f < 0) {
f = len + f;
- if (f < 0) {
- scope.result = Encode(-1);
- return;
- }
+ if (f < 0)
+ return Encode(-1);
}
fromIndex = (uint) f + 1;
}
@@ -747,281 +767,277 @@ void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, C
bool exists;
v = instance->getIndexed(k, &exists);
CHECK_EXCEPTION();
- if (exists && RuntimeHelpers::strictEqual(v, searchValue)) {
- scope.result = Encode(k);
- return;
- }
+ if (exists && RuntimeHelpers::strictEqual(v, searchValue))
+ return Encode(k);
}
- scope.result = Encode(-1);
+ return Encode(-1);
}
-void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedCallData cData(scope, 3);
- cData->args[2] = instance;
- cData->thisObject = callData->argument(1);
- ScopedValue v(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue r(scope);
+ Value *arguments = scope.alloc(3);
bool ok = true;
for (uint k = 0; ok && k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ arguments[0] = instance->getIndexed(k, &exists);
if (!exists)
continue;
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
- ok = scope.result.toBoolean();
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ r = callback->call(that, arguments, 3);
+ ok = r->toBoolean();
}
- scope.result = Encode(ok);
+ return Encode(ok);
}
-void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
- ScopedValue v(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ arguments[0] = instance->getIndexed(k, &exists);
if (!exists)
continue;
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
- if (scope.result.toBoolean()) {
- scope.result = Encode(true);
- return;
- }
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ result = callback->call(that, arguments, 3);
+ if (result->toBoolean())
+ return Encode(true);
}
- scope.result = Encode(false);
+ return Encode(false);
}
-void ArrayPrototype::method_forEach(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
- ScopedValue v(scope);
for (uint k = 0; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ arguments[0] = instance->getIndexed(k, &exists);
if (!exists)
continue;
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ callback->call(that, arguments, 3);
}
RETURN_UNDEFINED();
}
-void ArrayPrototype::method_map(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
ScopedArrayObject a(scope, scope.engine->newArrayObject());
a->arrayReserve(len);
a->setArrayLengthUnchecked(len);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
-
ScopedValue v(scope);
+ ScopedValue mapped(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
+
for (uint k = 0; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ arguments[0] = instance->getIndexed(k, &exists);
if (!exists)
continue;
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
- a->arraySet(k, scope.result);
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ mapped = callback->call(that, arguments, 3);
+ a->arraySet(k, mapped);
}
- scope.result = a.asReturnedValue();
+ return a.asReturnedValue();
}
-void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
ScopedArrayObject a(scope, scope.engine->newArrayObject());
a->arrayReserve(len);
- ScopedCallData cData(scope, 3);
- cData->thisObject = callData->argument(1);
- cData->args[2] = instance;
-
- ScopedValue v(scope);
+ ScopedValue selected(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
uint to = 0;
for (uint k = 0; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ arguments[0] = instance->getIndexed(k, &exists);
if (!exists)
continue;
- cData->args[0] = v;
- cData->args[1] = Primitive::fromDouble(k);
- callback->call(scope, cData);
- if (scope.result.toBoolean()) {
- a->arraySet(to, v);
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ selected = callback->call(that, arguments, 3);
+ if (selected->toBoolean()) {
+ a->arraySet(to, arguments[0]);
++to;
}
}
- scope.result = a.asReturnedValue();
+ return a.asReturnedValue();
}
-void ArrayPrototype::method_reduce(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
uint k = 0;
+ ScopedValue acc(scope);
ScopedValue v(scope);
- if (callData->argc > 1) {
- scope.result = callData->argument(1);
+ if (argc > 1) {
+ acc = argv[1];
} else {
bool kPresent = false;
while (k < len && !kPresent) {
v = instance->getIndexed(k, &kPresent);
if (kPresent)
- scope.result = v;
+ acc = v;
++k;
}
if (!kPresent)
THROW_TYPE_ERROR();
}
- ScopedCallData cData(scope, 4);
- cData->thisObject = Primitive::undefinedValue();
- cData->args[0] = scope.result;
- cData->args[3] = instance;
+ Value *arguments = scope.alloc(4);
while (k < len) {
bool kPresent;
v = instance->getIndexed(k, &kPresent);
if (kPresent) {
- cData->args[0] = scope.result;
- cData->args[1] = v;
- cData->args[2] = Primitive::fromDouble(k);
- callback->call(scope, cData);
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Primitive::fromDouble(k);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
}
++k;
}
+ return acc->asReturnedValue();
}
-void ArrayPrototype::method_reduceRight(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
uint len = instance->getLength();
- ScopedFunctionObject callback(scope, callData->argument(0));
- if (!callback)
+ if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
if (len == 0) {
- if (callData->argc == 1)
+ if (argc == 1)
THROW_TYPE_ERROR();
- scope.result = callData->argument(1);
- return;
+ return argv[1].asReturnedValue();
}
uint k = len;
+ ScopedValue acc(scope);
ScopedValue v(scope);
- if (callData->argc > 1) {
- scope.result = callData->argument(1);
+ if (argc > 1) {
+ acc = argv[1];
} else {
bool kPresent = false;
while (k > 0 && !kPresent) {
v = instance->getIndexed(k - 1, &kPresent);
if (kPresent)
- scope.result = v;
+ acc = v;
--k;
}
if (!kPresent)
THROW_TYPE_ERROR();
}
- ScopedCallData cData(scope, 4);
- cData->thisObject = Primitive::undefinedValue();
- cData->args[3] = instance;
+ Value *arguments = scope.alloc(4);
while (k > 0) {
bool kPresent;
v = instance->getIndexed(k - 1, &kPresent);
if (kPresent) {
- cData->args[0] = scope.result;
- cData->args[1] = v;
- cData->args[2] = Primitive::fromDouble(k - 1);
- callback->call(scope, cData);
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Primitive::fromDouble(k - 1);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
}
--k;
}
- scope.result = scope.result.asReturnedValue();
+ return acc->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index 689752433b..3825a600a2 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -70,38 +70,38 @@ struct ArrayCtor: FunctionObject
{
V4_OBJECT2(ArrayCtor, FunctionObject)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct ArrayPrototype: ArrayObject
{
void init(ExecutionEngine *engine, Object *ctor);
- static void method_isArray(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_toString(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_toLocaleString(const BuiltinFunction *builtin, Scope &, CallData *callData);
- static void method_concat(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_find(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_join(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_pop(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_push(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_reverse(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_shift(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_slice(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_sort(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_splice(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_unshift(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_indexOf(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_lastIndexOf(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_every(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_some(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_forEach(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_map(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_filter(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_reduce(const BuiltinFunction *, Scope &, CallData *callData);
- static void method_reduceRight(const BuiltinFunction *, Scope &, CallData *callData);
+ static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_pop(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_push(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reverse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_shift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index c8e9ebb2dd..eb83f902db 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -50,16 +50,16 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Boolean"));
}
-void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
{
- bool n = callData->argc ? callData->args[0].toBoolean() : false;
- scope.result = Encode(scope.engine->newBooleanObject(n));
+ bool n = argc ? argv[0].toBoolean() : false;
+ return Encode(that->engine()->newBooleanObject(n));
}
-void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- bool value = callData->argc ? callData->args[0].toBoolean() : 0;
- scope.result = Encode(value);
+ bool value = argc ? argv[0].toBoolean() : 0;
+ return Encode(value);
}
void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -73,31 +73,39 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(engine->id_valueOf(), method_valueOf);
}
-void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+static bool value(const Value *thisObject, bool *exception)
{
- bool result;
- if (callData->thisObject.isBoolean()) {
- result = callData->thisObject.booleanValue();
+ *exception = false;
+ if (thisObject->isBoolean()) {
+ return thisObject->booleanValue();
} else {
- const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>();
- if (!thisObject)
- THROW_TYPE_ERROR();
- result = thisObject->value();
+ const BooleanObject *that = thisObject->as<BooleanObject>();
+ if (that)
+ return that->value();
}
+ *exception = true;
+ return false;
+}
+
+ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ bool exception;
+ bool result = ::value(thisObject, &exception);
+ ExecutionEngine *v4 = b->engine();
+ if (exception)
+ return v4->throwTypeError();
- scope.result = result ? scope.engine->id_true() : scope.engine->id_false();
+ return Encode(result ? v4->id_true() : v4->id_false());
}
-void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- if (callData->thisObject.isBoolean()) {
- scope.result = callData->thisObject.asReturnedValue();
- return;
+ bool exception;
+ bool result = ::value(thisObject, &exception);
+ if (exception) {
+ ExecutionEngine *v4 = b->engine();
+ return v4->throwTypeError();
}
- const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>();
- if (!thisObject)
- THROW_TYPE_ERROR();
-
- scope.result = Encode(thisObject->value());
+ return Encode(result);
}
diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h
index ca2cf7d17a..3cf09b2667 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject
{
V4_OBJECT2(BooleanCtor, FunctionObject)
- static void construct(const Managed *, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct BooleanPrototype: BooleanObject
@@ -79,8 +79,8 @@ struct BooleanPrototype: BooleanObject
V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 6807c835b0..00d816fe91 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -48,59 +48,52 @@
#include "qv4errorobject_p.h"
#include "qv4string_p.h"
#include "qv4qmlcontext_p.h"
-#include "qv4profiling_p.h"
-#include <private/qqmljavascriptexpression_p.h>
using namespace QV4;
DEFINE_MANAGED_VTABLE(ExecutionContext);
-DEFINE_MANAGED_VTABLE(SimpleCallContext);
DEFINE_MANAGED_VTABLE(CallContext);
-DEFINE_MANAGED_VTABLE(WithContext);
DEFINE_MANAGED_VTABLE(CatchContext);
-DEFINE_MANAGED_VTABLE(GlobalContext);
-Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData)
+Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
{
- uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals);
- size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals);
-
- ExecutionEngine *v4 = engine();
- Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory);
- c->init(Heap::ExecutionContext::Type_CallContext);
+ Function *function = frame->v4Function;
+ Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
- c->v4Function = function;
+ uint nFormals = qMax(static_cast<uint>(frame->originalArgumentsCount), function->nFormals);
+ uint localsAndFormals = function->compiledFunction->nLocals + nFormals;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals);
- c->strictMode = function->isStrict();
- c->outer.set(v4, this->d());
+ ExecutionEngine *v4 = outer->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, function->internalClass);
+ c->init();
- c->compilationUnit = function->compilationUnit;
- c->lookups = function->compilationUnit->runtimeLookups;
- c->constantTable = function->compilationUnit->constants;
+ c->outer.set(v4, outer);
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
const CompiledData::Function *compiledFunction = function->compiledFunction;
uint nLocals = compiledFunction->nLocals;
c->locals.size = nLocals;
c->locals.alloc = localsAndFormals;
-#if QT_POINTER_SIZE == 8
- // memory allocated from the JS heap is 0 initialized, so skip the std::fill() below
+ // memory allocated from the JS heap is 0 initialized, so check if undefined is 0
Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
-#else
- if (nLocals)
- std::fill(c->locals.values, c->locals.values + nLocals, Primitive::undefinedValue());
-#endif
- c->callData = reinterpret_cast<CallData *>(c->locals.values + nLocals);
- ::memcpy(c->callData, callData, sizeof(CallData) - sizeof(Value) + static_cast<uint>(callData->argc) * sizeof(Value));
- if (callData->argc < static_cast<int>(compiledFunction->nFormals))
- std::fill(c->callData->args + c->callData->argc, c->callData->args + compiledFunction->nFormals, Primitive::undefinedValue());
+ 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)
+ args[i] = Encode::undefined();
return c;
}
-Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with)
+Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with)
{
- return engine()->memoryManager->alloc<WithContext>(d(), with);
+ Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext);
+ c->outer.set(engine(), d());
+ c->activation.set(engine(), with);
+
+ return c;
}
Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue)
@@ -120,25 +113,23 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
while (ctx) {
switch (ctx->d()->type) {
case Heap::ExecutionContext::Type_CallContext:
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
if (!activation) {
+ Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
if (!c->activation)
c->activation.set(scope.engine, scope.engine->newObject());
activation = c->activation;
}
break;
- }
case Heap::ExecutionContext::Type_QmlContext: {
// this is ugly, as it overrides the inner callcontext, but has to stay as long
// as bindings still get their own callcontext
- Heap::QmlContext *qml = static_cast<Heap::QmlContext *>(ctx->d());
- activation = qml->qml;
+ activation = ctx->d()->activation;
break;
}
case Heap::ExecutionContext::Type_GlobalContext: {
+ Q_ASSERT(scope.engine->globalObject->d() == ctx->d()->activation);
if (!activation)
- activation = scope.engine->globalObject;
+ activation = ctx->d()->activation;
break;
}
default:
@@ -155,99 +146,46 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
}
-void Heap::GlobalContext::init(ExecutionEngine *eng)
-{
- Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext);
- global.set(eng, eng->globalObject->d());
-}
-
void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
const Value &exceptionValue)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext);
outer.set(internalClass->engine, outerContext);
- strictMode = outer->strictMode;
- callData = outer->callData;
- lookups = outer->lookups;
- constantTable = outer->constantTable;
- compilationUnit = outer->compilationUnit;
this->exceptionVarName.set(internalClass->engine, exceptionVarName);
this->exceptionValue.set(internalClass->engine, exceptionValue);
}
-void Heap::WithContext::init(ExecutionContext *outerContext, Object *with)
-{
- Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext);
- outer.set(internalClass->engine, outerContext);
- callData = outer->callData;
- lookups = outer->lookups;
- constantTable = outer->constantTable;
- compilationUnit = outer->compilationUnit;
-
- withObject.set(internalClass->engine, with);
-}
-
-Identifier * const *SimpleCallContext::formals() const
-{
- return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() : 0;
-}
-
-unsigned int SimpleCallContext::formalCount() const
-{
- return d()->v4Function ? d()->v4Function->nFormals : 0;
-}
-
-Identifier * const *SimpleCallContext::variables() const
-{
- return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() + d()->v4Function->nFormals : 0;
-}
-
-unsigned int SimpleCallContext::variableCount() const
-{
- return d()->v4Function ? d()->v4Function->compiledFunction->nLocals : 0;
-}
-
-
-
bool ExecutionContext::deleteProperty(String *name)
{
name->makeIdentifier();
Identifier *id = name->identifier();
- Scope scope(this);
- ScopedContext ctx(scope, this);
- for (; ctx; ctx = ctx->d()->outer) {
- switch (ctx->d()->type) {
+ Heap::ExecutionContext *ctx = d();
+ for (; ctx; ctx = ctx->outer) {
+ switch (ctx->type) {
case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d());
+ Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
if (c->exceptionVarName->isEqualTo(name->d()))
return false;
break;
}
- case Heap::ExecutionContext::Type_WithContext: {
- ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject);
- if (withObject->hasProperty(name))
- return withObject->deleteProperty(name);
- break;
- }
- case Heap::ExecutionContext::Type_GlobalContext: {
- ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global);
- if (global->hasProperty(name))
- return global->deleteProperty(name);
- break;
- }
- case Heap::ExecutionContext::Type_CallContext:
- Q_FALLTHROUGH();
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
- uint index = c->v4Function->internalClass->find(id);
+ case Heap::ExecutionContext::Type_CallContext: {
+ Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
+ uint index = c->internalClass->find(id);
if (index < UINT_MAX)
// ### throw in strict mode?
return false;
- ScopedObject qml(scope, c->activation);
- if (qml && qml->hasProperty(name))
- return qml->deleteProperty(name);
+ Q_FALLTHROUGH();
+ }
+ case Heap::ExecutionContext::Type_WithContext:
+ case Heap::ExecutionContext::Type_GlobalContext: {
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject object(scope, ctx->activation);
+ if (object && object->hasProperty(name))
+ return object->deleteProperty(name);
+ }
break;
}
case Heap::ExecutionContext::Type_QmlContext:
@@ -256,305 +194,165 @@ bool ExecutionContext::deleteProperty(String *name)
}
}
- if (d()->strictMode)
- engine()->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(name->toQString()));
- return true;
-}
-
-// Do a standard call with this execution context as the outer scope
-void ExecutionContext::call(Scope &scope, CallData *callData, Function *function, const FunctionObject *f)
-{
- ExecutionContextSaver ctxSaver(scope);
-
- Scoped<CallContext> ctx(scope, newCallContext(function, callData));
- if (f)
- ctx->d()->function.set(scope.engine, f->d());
- scope.engine->pushContext(ctx);
-
- scope.result = Q_V4_PROFILE(scope.engine, function);
-
- if (function->hasQmlDependencies)
- QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope);
+ return !engine()->currentStackFrame->v4Function->isStrict();
}
-// Do a simple, fast call with this execution context as the outer scope
-void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Function *function)
-{
- Q_ASSERT(function->canUseSimpleFunction());
-
- ExecutionContextSaver ctxSaver(scope);
-
- SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext();
-
- ctx->strictMode = function->isStrict();
- ctx->callData = callData;
- ctx->v4Function = function;
- ctx->compilationUnit = function->compilationUnit;
- ctx->lookups = function->compilationUnit->runtimeLookups;
- ctx->constantTable = function->compilationUnit->constants;
- ctx->outer.set(scope.engine, this->d());
- for (int i = callData->argc; i < (int)function->nFormals; ++i)
- callData->args[i] = Encode::undefined();
-
- scope.engine->pushContext(ctx);
- Q_ASSERT(scope.engine->current == ctx);
-
- scope.result = Q_V4_PROFILE(scope.engine, function);
-
- if (function->hasQmlDependencies)
- QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope);
- scope.engine->memoryManager->freeSimpleCallContext();
-}
-
-void ExecutionContext::setProperty(String *name, const Value &value)
+ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
{
name->makeIdentifier();
Identifier *id = name->identifier();
- Scope scope(this);
- ScopedContext ctx(scope, this);
- ScopedObject activation(scope);
+ QV4::ExecutionEngine *v4 = engine();
+ Heap::ExecutionContext *ctx = d();
- for (; ctx; ctx = ctx->d()->outer) {
- activation = (Object *)0;
- switch (ctx->d()->type) {
+ for (; ctx; ctx = ctx->outer) {
+ switch (ctx->type) {
case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d());
+ Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
if (c->exceptionVarName->isEqualTo(name->d())) {
- c->exceptionValue.set(scope.engine, value);
- return;
+ c->exceptionValue.set(v4, value);
+ return NoError;
}
break;
}
case Heap::ExecutionContext::Type_WithContext: {
- ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject);
+ Scope scope(v4);
+ ScopedObject w(scope, ctx->activation);
if (w->hasProperty(name)) {
- w->put(name, value);
- return;
+ if (!w->put(name, value))
+ return TypeError;
+ return NoError;
}
break;
}
- case Heap::ExecutionContext::Type_GlobalContext: {
- activation = static_cast<Heap::GlobalContext *>(ctx->d())->global;
- break;
+ case Heap::ExecutionContext::Type_CallContext: {
+ Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
+ uint index = c->internalClass->find(id);
+ if (index < UINT_MAX) {
+ static_cast<Heap::CallContext *>(c)->locals.set(v4, index, value);
+ return NoError;
+ }
}
- case Heap::ExecutionContext::Type_CallContext:
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
- if (c->v4Function) {
- uint index = c->v4Function->internalClass->find(id);
- if (index < UINT_MAX) {
- if (index < c->v4Function->nFormals) {
- c->callData->args[c->v4Function->nFormals - index - 1] = value;
- } else {
- Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext);
- index -= c->v4Function->nFormals;
- static_cast<Heap::CallContext *>(c)->locals.set(scope.engine, index, value);
- }
- return;
+ Q_FALLTHROUGH();
+ case Heap::ExecutionContext::Type_GlobalContext:
+ if (ctx->activation) {
+ uint member = ctx->activation->internalClass->find(id);
+ if (member < UINT_MAX) {
+ Scope scope(v4);
+ ScopedObject a(scope, ctx->activation);
+ if (!a->putValue(member, value))
+ return TypeError;
+ return NoError;
}
}
- activation = c->activation;
break;
- }
case Heap::ExecutionContext::Type_QmlContext: {
- activation = static_cast<Heap::QmlContext *>(ctx->d())->qml;
- activation->put(name, value);
- return;
+ Scope scope(v4);
+ ScopedObject activation(scope, ctx->activation);
+ if (!activation->put(name, value))
+ return TypeError;
+ return NoError;
}
}
- if (activation) {
- uint member = activation->internalClass()->find(id);
- if (member < UINT_MAX) {
- activation->putValue(member, value);
- return;
- }
- }
}
- if (d()->strictMode || name->equals(engine()->id_this())) {
- ScopedValue n(scope, name->asReturnedValue());
- engine()->throwReferenceError(n);
- return;
- }
- engine()->globalObject->put(name, value);
+ return RangeError;
}
ReturnedValue ExecutionContext::getProperty(String *name)
{
- Scope scope(this);
- ScopedValue v(scope);
name->makeIdentifier();
- if (name->equals(engine()->id_this()))
- return thisObject().asReturnedValue();
-
- ScopedContext ctx(scope, this);
- for (; ctx; ctx = ctx->d()->outer) {
- switch (ctx->d()->type) {
+ Heap::ExecutionContext *ctx = d();
+ for (; ctx; ctx = ctx->outer) {
+ switch (ctx->type) {
case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d());
+ Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
if (c->exceptionVarName->isEqualTo(name->d()))
return c->exceptionValue.asReturnedValue();
break;
}
- case Heap::ExecutionContext::Type_WithContext: {
- ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject);
- bool hasProperty = false;
- v = w->get(name, &hasProperty);
- if (hasProperty) {
- return v->asReturnedValue();
- }
- break;
- }
- case Heap::ExecutionContext::Type_GlobalContext: {
- ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global);
- bool hasProperty = false;
- v = global->get(name, &hasProperty);
- if (hasProperty)
- return v->asReturnedValue();
- break;
- }
- case Heap::ExecutionContext::Type_CallContext:
- Q_FALLTHROUGH();
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
-
- name->makeIdentifier();
+ case Heap::ExecutionContext::Type_CallContext: {
+ Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
Identifier *id = name->identifier();
- uint index = c->v4Function->internalClass->find(id);
- if (index < UINT_MAX) {
- if (index < c->v4Function->nFormals)
- return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue();
- if (c->type == Heap::ExecutionContext::Type_CallContext)
- return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue();
- }
- ScopedObject activation(scope, c->activation);
- if (activation) {
+ uint index = c->internalClass->find(id);
+ if (index < UINT_MAX)
+ return c->locals[index].asReturnedValue();
+ Q_FALLTHROUGH();
+ }
+ case Heap::ExecutionContext::Type_WithContext:
+ case Heap::ExecutionContext::Type_GlobalContext:
+ case Heap::ExecutionContext::Type_QmlContext: {
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject activation(scope, ctx->activation);
bool hasProperty = false;
- v = activation->get(name, &hasProperty);
+ ReturnedValue v = activation->get(name, &hasProperty);
if (hasProperty)
- return v->asReturnedValue();
- }
-
- if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) {
- if (name->equals(ScopedString(scope, c->v4Function->name())))
- if (auto func = static_cast<Heap::CallContext *>(c)->function)
- return func->asReturnedValue();
+ return v;
}
break;
}
- case Heap::ExecutionContext::Type_QmlContext: {
- ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml);
- bool hasProperty = false;
- v = qml->get(name, &hasProperty);
- if (hasProperty)
- return v->asReturnedValue();
- break;
- }
}
}
- ScopedValue n(scope, name);
- return engine()->throwReferenceError(n);
+ return engine()->throwReferenceError(*name);
}
ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
{
- Scope scope(this);
- ScopedValue v(scope);
base->setM(0);
name->makeIdentifier();
- if (name->equals(engine()->id_this()))
- return thisObject().asReturnedValue();
-
- ScopedContext ctx(scope, this);
- for (; ctx; ctx = ctx->d()->outer) {
- switch (ctx->d()->type) {
+ Heap::ExecutionContext *ctx = d();
+ for (; ctx; ctx = ctx->outer) {
+ switch (ctx->type) {
case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d());
+ Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
if (c->exceptionVarName->isEqualTo(name->d()))
return c->exceptionValue.asReturnedValue();
break;
}
- case Heap::ExecutionContext::Type_WithContext: {
- ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject);
- bool hasProperty = false;
- v = w->get(name, &hasProperty);
- if (hasProperty) {
- base->setM(w->d());
- return v->asReturnedValue();
- }
- break;
- }
- case Heap::ExecutionContext::Type_GlobalContext: {
- ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global);
- bool hasProperty = false;
- v = global->get(name, &hasProperty);
- if (hasProperty)
- return v->asReturnedValue();
- break;
- }
- case Heap::ExecutionContext::Type_CallContext:
- Q_FALLTHROUGH();
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
-
+ case Heap::ExecutionContext::Type_CallContext: {
+ Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
name->makeIdentifier();
Identifier *id = name->identifier();
- uint index = c->v4Function->internalClass->find(id);
- if (index < UINT_MAX) {
- if (index < c->v4Function->nFormals)
- return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue();
- if (c->type == Heap::ExecutionContext::Type_CallContext)
- return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue();
- }
- ScopedObject activation(scope, c->activation);
- if (activation) {
+ uint index = c->internalClass->find(id);
+ if (index < UINT_MAX)
+ return c->locals[index].asReturnedValue();
+ Q_FALLTHROUGH();
+ }
+ case Heap::ExecutionContext::Type_GlobalContext: {
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject activation(scope, ctx->activation);
bool hasProperty = false;
- v = activation->get(name, &hasProperty);
+ ReturnedValue v = activation->get(name, &hasProperty);
if (hasProperty)
- return v->asReturnedValue();
- }
-
- if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) {
- if (name->equals(ScopedString(scope, c->v4Function->name())))
- if (auto func = static_cast<Heap::CallContext *>(c)->function)
- return func->asReturnedValue();
+ return v;
}
break;
}
+ case Heap::ExecutionContext::Type_WithContext:
case Heap::ExecutionContext::Type_QmlContext: {
- ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml);
+ Scope scope(this);
+ ScopedObject o(scope, ctx->activation);
bool hasProperty = false;
- v = qml->get(name, &hasProperty);
+ ReturnedValue v = o->get(name, &hasProperty);
if (hasProperty) {
- base->setM(qml->d());
- return v->asReturnedValue();
+ base->setM(o->d());
+ return v;
}
break;
}
}
}
- ScopedValue n(scope, name);
- return engine()->throwReferenceError(n);
+ return engine()->throwReferenceError(*name);
}
-Function *ExecutionContext::getFunction() const
+void Heap::CallContext::setArg(uint index, Value v)
{
- Scope scope(engine());
- ScopedContext it(scope, this->d());
- for (; it; it = it->d()->outer) {
- if (const SimpleCallContext *callCtx = it->asSimpleCallContext())
- return callCtx->d()->v4Function;
- else if (it->asCatchContext() || it->asWithContext())
- continue; // look in the parent context for a FunctionObject
- else
- break;
- }
-
- return 0;
+ locals.set(internalClass->engine, locals.size + index, v);
}
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index a854c324d0..4efd0bc899 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -68,59 +68,66 @@ struct Function;
struct Function;
struct Identifier;
struct CallContext;
-struct SimpleCallContext;
struct CatchContext;
-struct WithContext;
struct QmlContext;
struct QQmlContextWrapper;
-// Attention: Make sure that this structure is the same size on 32-bit and 64-bit
-// architecture or you'll have to change the JIT code.
struct CallData
{
- // below is to be compatible with Value. Initialize tag to 0
-#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN
- uint tag;
-#endif
- int argc;
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- uint tag;
-#endif
+ enum Offsets {
+ Function = 0,
+ Context = 1,
+ Accumulator = 2,
+ This = 3,
+ Argc = 4
+ };
+
+ Value function;
+ Value context;
+ Value accumulator;
+ Value thisObject;
+ Value _argc;
+
+ int argc() const {
+ Q_ASSERT(_argc.isInteger());
+ return _argc.int_32();
+ }
+
+ void setArgc(int argc) {
+ Q_ASSERT(argc >= 0);
+ _argc.setInt_32(argc);
+ }
+
inline ReturnedValue argument(int i) const {
- return i < argc ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
+ return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
}
- Value thisObject;
Value args[1];
+
+ static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
};
Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
-Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 8);
-Q_STATIC_ASSERT(offsetof(CallData, args) == 16);
+Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value));
namespace Heap {
struct QmlContext;
#define ExecutionContextMembers(class, Member) \
- Member(class, NoMark, CallData *, callData) \
Member(class, Pointer, ExecutionContext *, outer) \
- Member(class, NoMark, Lookup *, lookups) \
- Member(class, NoMark, const QV4::Value *, constantTable) \
- Member(class, NoMark, CompiledData::CompilationUnitBase *, compilationUnit) \
- Member(class, NoMark, int, lineNumber) // as member of non-pointer size this has to come last to preserve the ability to
- // translate offsetof of it between 64-bit and 32-bit.
+ Member(class, Pointer, Object *, activation)
DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
- DECLARE_MARK_TABLE(ExecutionContext);
+ DECLARE_MARKOBJECTS(ExecutionContext);
enum ContextType {
Type_GlobalContext = 0x1,
Type_CatchContext = 0x2,
Type_WithContext = 0x3,
Type_QmlContext = 0x4,
- Type_SimpleCallContext = 0x5,
- Type_CallContext = 0x6
+ Type_CallContext = 0x5
};
void init(ContextType t)
@@ -128,105 +135,63 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
Base::init();
type = t;
- lineNumber = -1;
}
- quint8 type;
- bool strictMode : 8;
+ quint32 type : 8;
+ quint32 nArgs : 24;
#if QT_POINTER_SIZE == 8
- quint8 padding_[6];
-#else
- quint8 padding_[2];
+ quint8 padding_[4];
#endif
};
V4_ASSERT_IS_TRIVIAL(ExecutionContext)
Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE);
-
-#define SimpleCallContextMembers(class, Member) \
- Member(class, Pointer, Object *, activation) \
- Member(class, NoMark, QV4::Function *, v4Function)
-
-DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) {
- DECLARE_MARK_TABLE(SimpleCallContext);
-
- void init(ContextType t = Type_SimpleCallContext)
- {
- ExecutionContext::init(t);
- }
-
- inline unsigned int formalParameterCount() const;
+Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == 0);
+Q_STATIC_ASSERT(offsetof(ExecutionContextData, activation) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE);
-};
-V4_ASSERT_IS_TRIVIAL(SimpleCallContext)
-Q_STATIC_ASSERT(std::is_standard_layout<SimpleCallContextData>::value);
-Q_STATIC_ASSERT(offsetof(SimpleCallContextData, activation) == 0);
-Q_STATIC_ASSERT(offsetof(SimpleCallContextData, v4Function) == offsetof(SimpleCallContextData, activation) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(sizeof(SimpleCallContextData) == 2 * QT_POINTER_SIZE);
-Q_STATIC_ASSERT(sizeof(SimpleCallContext) == sizeof(ExecutionContext) + sizeof(SimpleCallContextData));
-
-#if QT_POINTER_SIZE == 8
#define CallContextMembers(class, Member) \
Member(class, Pointer, FunctionObject *, function) \
Member(class, ValueArray, ValueArray, locals)
-#else
-#define CallContextMembers(class, Member) \
- Member(class, Pointer, FunctionObject *, function) \
- Member(class, NoMark, void *, padding) \
- Member(class, ValueArray, ValueArray, locals)
-#endif
-DECLARE_HEAP_OBJECT(CallContext, SimpleCallContext) {
- DECLARE_MARK_TABLE(CallContext);
+DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) {
+ DECLARE_MARKOBJECTS(CallContext);
- using SimpleCallContext::formalParameterCount;
-};
+ void init()
+ {
+ ExecutionContext::init(Type_CallContext);
+ }
+ int argc() const {
+ return static_cast<int>(nArgs);
+ }
+ const Value *args() const {
+ return locals.data() + locals.size;
+ }
+ void setArg(uint index, Value v);
+};
+V4_ASSERT_IS_TRIVIAL(CallContext)
Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value);
Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0);
-// IMPORTANT: we cannot do offsetof(CallContextData, locals) in the JIT as the offset does not scale with
-// the pointer size. On 32-bit ARM the offset of the ValueArray is aligned to 8 bytes, on 32-bit x86 for
-// example it is not. Therefore we have a padding in place and always have a distance of 8 bytes.
-Q_STATIC_ASSERT(offsetof(CallContextData, locals) == offsetof(CallContextData, function) + 8);
-
-#define GlobalContextMembers(class, Member) \
- Member(class, Pointer, Object *, global)
-
-DECLARE_HEAP_OBJECT(GlobalContext, ExecutionContext) {
- DECLARE_MARK_TABLE(GlobalContext);
-
- void init(ExecutionEngine *engine);
-};
-V4_ASSERT_IS_TRIVIAL(GlobalContext)
+//### The following size check fails on Win8. With the ValueArray at the end of the
+// CallContextMembers, it doesn't look very useful.
+//#if defined(Q_PROCESSOR_ARM_32) && !defined(Q_OS_IOS)
+//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData) + QT_POINTER_SIZE);
+//#else
+//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData));
+//#endif
#define CatchContextMembers(class, Member) \
Member(class, Pointer, String *, exceptionVarName) \
Member(class, HeapValue, HeapValue, exceptionValue)
DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) {
- DECLARE_MARK_TABLE(CatchContext);
+ DECLARE_MARKOBJECTS(CatchContext);
void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
};
V4_ASSERT_IS_TRIVIAL(CatchContext)
-#define WithContextMembers(class, Member) \
- Member(class, Pointer, Object *, withObject)
-
-DECLARE_HEAP_OBJECT(WithContext, ExecutionContext) {
- DECLARE_MARK_TABLE(WithContext);
-
- void init(ExecutionContext *outerContext, Object *with);
-};
-V4_ASSERT_IS_TRIVIAL(WithContext)
-
}
struct Q_QML_EXPORT ExecutionContext : public Managed
@@ -239,68 +204,39 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
Q_MANAGED_TYPE(ExecutionContext)
V4_INTERNALCLASS(ExecutionContext)
- Heap::CallContext *newCallContext(Function *f, CallData *callData);
- Heap::WithContext *newWithContext(Heap::Object *with);
+ static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
+ Heap::ExecutionContext *newWithContext(Heap::Object *with);
Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue);
void createMutableBinding(String *name, bool deletable);
- void setProperty(String *name, const Value &value);
+ enum Error {
+ NoError,
+ TypeError,
+ RangeError
+ };
+
+ Error setProperty(String *name, const Value &value);
+
ReturnedValue getProperty(String *name);
ReturnedValue getPropertyAndBase(String *name, Value *base);
bool deleteProperty(String *name);
- inline SimpleCallContext *asSimpleCallContext();
- inline const SimpleCallContext *asSimpleCallContext() const;
- inline const CatchContext *asCatchContext() const;
- inline const WithContext *asWithContext() const;
+ inline CallContext *asCallContext();
+ inline const CallContext *asCallContext() const;
+};
- Function *getFunction() const;
+struct Q_QML_EXPORT CallContext : public ExecutionContext
+{
+ V4_MANAGED(CallContext, ExecutionContext)
+ V4_INTERNALCLASS(CallContext)
- Value &thisObject() const {
- return d()->callData->thisObject;
- }
int argc() const {
- return d()->callData->argc;
+ return d()->argc();
}
const Value *args() const {
- return d()->callData->args;
- }
- ReturnedValue argument(int i) const {
- return d()->callData->argument(i);
+ return d()->args();
}
-
- void call(Scope &scope, CallData *callData, QV4::Function *function, const QV4::FunctionObject *f = 0);
- void simpleCall(Scope &scope, CallData *callData, QV4::Function *function);
-};
-
-struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext
-{
- V4_MANAGED(SimpleCallContext, ExecutionContext)
- V4_INTERNALCLASS(SimpleCallContext)
-
- // formals are in reverse order
- Identifier * const *formals() const;
- unsigned int formalCount() const;
- Identifier * const *variables() const;
- unsigned int variableCount() const;
-
- inline ReturnedValue argument(int i) const;
-};
-
-inline ReturnedValue SimpleCallContext::argument(int i) const {
- return i < argc() ? args()[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
-}
-
-struct Q_QML_EXPORT CallContext : public SimpleCallContext
-{
- V4_MANAGED(CallContext, SimpleCallContext)
-};
-
-struct GlobalContext : public ExecutionContext
-{
- V4_MANAGED(GlobalContext, ExecutionContext)
-
};
struct CatchContext : public ExecutionContext
@@ -308,29 +244,14 @@ struct CatchContext : public ExecutionContext
V4_MANAGED(CatchContext, ExecutionContext)
};
-struct WithContext : public ExecutionContext
-{
- V4_MANAGED(WithContext, ExecutionContext)
-};
-
-inline SimpleCallContext *ExecutionContext::asSimpleCallContext()
-{
- return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<SimpleCallContext *>(this) : 0;
-}
-
-inline const SimpleCallContext *ExecutionContext::asSimpleCallContext() const
-{
- return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const SimpleCallContext *>(this) : 0;
-}
-
-inline const CatchContext *ExecutionContext::asCatchContext() const
+inline CallContext *ExecutionContext::asCallContext()
{
- return d()->type == Heap::ExecutionContext::Type_CatchContext ? static_cast<const CatchContext *>(this) : 0;
+ return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : 0;
}
-inline const WithContext *ExecutionContext::asWithContext() const
+inline const CallContext *ExecutionContext::asCallContext() const
{
- return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast<const WithContext *>(this) : 0;
+ return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<const CallContext *>(this) : 0;
}
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index f1405e08ee..397ef1cfec 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -54,34 +54,31 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
}
-void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- Scoped<ArrayBuffer> buffer(scope, callData->argument(0));
- if (!buffer) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ Scope scope(f->engine());
+ Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue());
+ if (!buffer)
+ return scope.engine->throwTypeError();
- double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0;
+ double bo = argc > 1 ? argv[1].toNumber() : 0;
uint byteOffset = (uint)bo;
uint bufferLength = buffer->d()->data->size;
- double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber();
+ double bl = argc < 3 || argv[2].isUndefined() ? (bufferLength - bo) : argv[2].toNumber();
uint byteLength = (uint)bl;
- if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) {
- scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
- return;
- }
+ if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength)
+ return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>());
a->d()->buffer.set(scope.engine, buffer->d());
a->d()->byteLength = byteLength;
a->d()->byteOffset = byteOffset;
- scope.result = a.asReturnedValue();
+ return a.asReturnedValue();
}
-void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -122,84 +119,84 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0);
}
-void DataViewPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<DataView> v(scope, callData->thisObject);
+ const DataView *v = thisObject->as<DataView>();
if (!v)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
- scope.result = v->d()->buffer;
+ return v->d()->buffer->asReturnedValue();
}
-void DataViewPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<DataView> v(scope, callData->thisObject);
+ const DataView *v = thisObject->as<DataView>();
if (!v)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
- scope.result = Encode(v->d()->byteLength);
+ return Encode(v->d()->byteLength);
}
-void DataViewPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<DataView> v(scope, callData->thisObject);
+ const DataView *v = thisObject->as<DataView>();
if (!v)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
- scope.result = Encode(v->d()->byteOffset);
+ return Encode(v->d()->byteOffset);
}
template <typename T>
-void DataViewPrototype::method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
T t = T(v->d()->buffer->data->data()[idx]);
- scope.result = Encode((int)t);
+ return Encode((int)t);
}
template <typename T>
-void DataViewPrototype::method_get(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
- bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean();
+ bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
T t = littleEndian
? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx);
- scope.result = Encode(t);
+ return Encode(t);
}
template <typename T>
-void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
- bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean();
+ bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
if (sizeof(T) == 4) {
// float
@@ -210,7 +207,7 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C
u.i = littleEndian
? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx);
- scope.result = Encode(u.f);
+ return Encode(u.f);
} else {
Q_ASSERT(sizeof(T) == 8);
union {
@@ -220,43 +217,43 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C
u.i = littleEndian
? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx)
: qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx);
- scope.result = Encode(u.d);
+ return Encode(u.d);
}
}
template <typename T>
-void DataViewPrototype::method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
- int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0;
+ int val = argc >= 2 ? argv[1].toInt32() : 0;
v->d()->buffer->data->data()[idx] = (char)val;
RETURN_UNDEFINED();
}
template <typename T>
-void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
- int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0;
+ int val = argc >= 2 ? argv[1].toInt32() : 0;
- bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean();
+ bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
if (littleEndian)
qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
@@ -267,19 +264,19 @@ void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallDa
}
template <typename T>
-void DataViewPrototype::method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DataView> v(scope, callData->thisObject);
- if (!v || callData->argc < 1)
- THROW_TYPE_ERROR();
- double l = callData->args[0].toNumber();
+ const DataView *v = thisObject->as<DataView>();
+ if (!v || argc < 1)
+ return b->engine()->throwTypeError();
+ double l = argv[0].toNumber();
uint idx = (uint)l;
if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- THROW_TYPE_ERROR();
+ return b->engine()->throwTypeError();
idx += v->d()->byteOffset;
- double val = callData->argc >= 2 ? callData->args[1].toNumber() : qt_qnan();
- bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean();
+ double val = argc >= 2 ? argv[1].toNumber() : qt_qnan();
+ bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
if (sizeof(T) == 4) {
// float
diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h
index 5c50df4655..1e07d85118 100644
--- a/src/qml/jsruntime/qv4dataview_p.h
+++ b/src/qml/jsruntime/qv4dataview_p.h
@@ -69,7 +69,7 @@ struct DataViewCtor : FunctionObject {
Member(class, NoMark, uint, byteOffset)
DECLARE_HEAP_OBJECT(DataView, Object) {
- DECLARE_MARK_TABLE(DataView);
+ DECLARE_MARKOBJECTS(DataView);
void init() { Object::init(); }
};
@@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject
{
V4_OBJECT2(DataViewCtor, FunctionObject)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct DataView : Object
@@ -93,21 +93,21 @@ struct DataViewPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_getChar(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_get(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_getFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_setChar(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <typename T>
- static void method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_setFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 3aa20931ad..b8392d27e9 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -43,6 +43,7 @@
#include "qv4scopedvalue_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
#include <QtCore/QDebug>
#include <QtCore/QDateTime>
@@ -744,15 +745,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Date"));
}
-void DateCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
{
double t = 0;
- if (callData->argc == 0)
+ if (argc == 0)
t = currentTime();
- else if (callData->argc == 1) {
- ScopedValue arg(scope, callData->args[0]);
+ else if (argc == 1) {
+ Scope scope(that->engine());
+ ScopedValue arg(scope, argv[0]);
if (DateObject *d = arg->as<DateObject>()) {
t = d->date();
} else {
@@ -766,26 +768,26 @@ void DateCtor::construct(const Managed *, Scope &scope, CallData *callData)
}
else { // d.argc > 1
- double year = callData->args[0].toNumber();
- double month = callData->args[1].toNumber();
- double day = callData->argc >= 3 ? callData->args[2].toNumber() : 1;
- double hours = callData->argc >= 4 ? callData->args[3].toNumber() : 0;
- double mins = callData->argc >= 5 ? callData->args[4].toNumber() : 0;
- double secs = callData->argc >= 6 ? callData->args[5].toNumber() : 0;
- double ms = callData->argc >= 7 ? callData->args[6].toNumber() : 0;
+ double year = argv[0].toNumber();
+ double month = argv[1].toNumber();
+ double day = argc >= 3 ? argv[2].toNumber() : 1;
+ double hours = argc >= 4 ? argv[3].toNumber() : 0;
+ double mins = argc >= 5 ? argv[4].toNumber() : 0;
+ double secs = argc >= 6 ? argv[5].toNumber() : 0;
+ double ms = argc >= 7 ? argv[6].toNumber() : 0;
if (year >= 0 && year <= 99)
year += 1900;
t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
t = TimeClip(UTC(t));
}
- scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t)));
+ return Encode(that->engine()->newDateObject(Primitive::fromDouble(t)));
}
-void DateCtor::call(const Managed *m, Scope &scope, CallData *)
+ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int)
{
double t = currentTime();
- scope.result = static_cast<const DateCtor *>(m)->engine()->newString(ToString(t));
+ return m->engine()->newString(ToString(t))->asReturnedValue();
}
void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -862,459 +864,518 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1);
}
-double DatePrototype::getThisDate(Scope &scope, CallData *callData)
+double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
{
- if (DateObject *thisObject = callData->thisObject.as<DateObject>())
- return thisObject->date();
- else {
- scope.engine->throwTypeError();
- return 0;
- }
+ if (const DateObject *that = thisObject->as<DateObject>())
+ return that->date();
+ v4->throwTypeError();
+ return 0;
}
-void DatePrototype::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
- scope.result = Encode(qt_qnan());
+ if (!argc)
+ return Encode(qt_qnan());
else
- scope.result = Encode(ParseString(callData->args[0].toQString()));
+ return Encode(ParseString(argv[0].toQString()));
}
-void DatePrototype::method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- const int numArgs = callData->argc;
+ const int numArgs = argc;
if (numArgs >= 2) {
- double year = callData->args[0].toNumber();
- double month = callData->args[1].toNumber();
- double day = numArgs >= 3 ? callData->args[2].toNumber() : 1;
- double hours = numArgs >= 4 ? callData->args[3].toNumber() : 0;
- double mins = numArgs >= 5 ? callData->args[4].toNumber() : 0;
- double secs = numArgs >= 6 ? callData->args[5].toNumber() : 0;
- double ms = numArgs >= 7 ? callData->args[6].toNumber() : 0;
+ double year = argv[0].toNumber();
+ double month = argv[1].toNumber();
+ double day = numArgs >= 3 ? argv[2].toNumber() : 1;
+ double hours = numArgs >= 4 ? argv[3].toNumber() : 0;
+ double mins = numArgs >= 5 ? argv[4].toNumber() : 0;
+ double secs = numArgs >= 6 ? argv[5].toNumber() : 0;
+ double ms = numArgs >= 7 ? argv[6].toNumber() : 0;
if (year >= 0 && year <= 99)
year += 1900;
double t = MakeDate(MakeDay(year, month, day),
MakeTime(hours, mins, secs, ms));
- scope.result = Encode(TimeClip(t));
- return;
+ return Encode(TimeClip(t));
}
RETURN_UNDEFINED();
}
-void DatePrototype::method_now(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_now(const FunctionObject *, const Value *, const Value *, int)
{
- Q_UNUSED(callData);
- double t = currentTime();
- scope.result = Encode(t);
+ return Encode(currentTime());
}
-void DatePrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToString(t)));
}
-void DatePrototype::method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToDateString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToDateString(t)));
}
-void DatePrototype::method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToTimeString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToTimeString(t)));
}
-void DatePrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToLocaleString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToLocaleString(t)));
}
-void DatePrototype::method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toLocaleDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToLocaleDateString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToLocaleDateString(t)));
}
-void DatePrototype::method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toLocaleTimeString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = scope.engine->newString(ToLocaleTimeString(t));
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(v4->newString(ToLocaleTimeString(t)));
}
-void DatePrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = Encode(t);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(t);
}
-void DatePrototype::method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getTime(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
- scope.result = Encode(t);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
+ return Encode(t);
}
-void DatePrototype::method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = YearFromTime(LocalTime(t)) - 1900;
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = YearFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = YearFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = MonthFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = MonthFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = DateFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCDate(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = DateFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = WeekDay(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCDay(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = WeekDay(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = HourFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCHours(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = HourFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = MinFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = MinFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = SecFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = SecFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = msFromTime(LocalTime(t));
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = msFromTime(t);
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- double t = getThisDate(scope, callData);
+ ExecutionEngine *v4 = b->engine();
+ double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
t = (t - LocalTime(t)) / msPerMinute;
- scope.result = Encode(t);
+ return Encode(t);
}
-void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setTime(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DateObject> self(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- double t = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
+ double t = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
self->setDate(TimeClip(t));
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<DateObject> self(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double ms = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCMilliseconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- CHECK_EXCEPTION();
- double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double ms = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))));
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
- double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double sec = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCSeconds(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber();
+ double sec = argc ? argv[0].toNumber() : qt_qnan();
+ double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double min = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
- double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber();
- CHECK_EXCEPTION();
- double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double min = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCMinutes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- double min = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber();
- double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber();
+ double min = argc ? argv[0].toNumber() : qt_qnan();
+ double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber();
+ double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
- double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber();
- CHECK_EXCEPTION();
- double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber();
- CHECK_EXCEPTION();
- double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double hour = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCHours(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber();
- double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber();
- double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber();
+ double hour = argc ? argv[0].toNumber() : qt_qnan();
+ double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber();
+ double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber();
+ double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double date = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double date = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCDate(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- CHECK_EXCEPTION();
- double date = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double date = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
- double month = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
- double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber();
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double month = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCMonth(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- double month = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber();
+ double month = argc ? argv[0].toNumber() : qt_qnan();
+ double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
if (std::isnan(t))
t = 0;
else
t = LocalTime(t);
- double year = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double year = argc ? argv[0].toNumber() : qt_qnan();
double r;
if (std::isnan(year)) {
r = qt_qnan();
@@ -1326,53 +1387,60 @@ void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallDa
r = TimeClip(r);
}
self->setDate(r);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setUTCFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- double year = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber();
- double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber();
+ double year = argc ? argv[0].toNumber() : qt_qnan();
+ double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber();
+ double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = LocalTime(self->date());
- CHECK_EXCEPTION();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
if (std::isnan(t))
t = 0;
- double year = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- CHECK_EXCEPTION();
- double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber();
- CHECK_EXCEPTION();
- double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber();
- CHECK_EXCEPTION();
+ double year = argc ? argv[0].toNumber() : qt_qnan();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
+ if (v4->hasException)
+ return QV4::Encode::undefined();
t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
self->setDate(t);
- scope.result = Encode(self->date());
+ return Encode(self->date());
}
-void DatePrototype::method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toUTCString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
- scope.result = scope.engine->newString(ToUTCString(t));
+ return Encode(v4->newString(ToUTCString(t)));
}
static void addZeroPrefixedInt(QString &str, int num, int nDigits)
@@ -1388,21 +1456,22 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits)
}
}
-void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- DateObject *self = callData->thisObject.as<DateObject>();
+ ExecutionEngine *v4 = b->engine();
+ DateObject *self = const_cast<DateObject *>(thisObject->as<DateObject>());
if (!self)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
double t = self->date();
if (!std::isfinite(t))
- RETURN_RESULT(scope.engine->throwRangeError(callData->thisObject));
+ RETURN_RESULT(v4->throwRangeError(*thisObject));
QString result;
int year = (int)YearFromTime(t);
if (year < 0 || year > 9999) {
if (qAbs(year) >= 1000000)
- RETURN_RESULT(scope.engine->newString(QStringLiteral("Invalid Date")));
+ RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date")));
result += year < 0 ? QLatin1Char('-') : QLatin1Char('+');
year = qAbs(year);
addZeroPrefixedInt(result, year, 6);
@@ -1423,29 +1492,30 @@ void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, Ca
addZeroPrefixedInt(result, msFromTime(t), 3);
result += QLatin1Char('Z');
- scope.result = scope.engine->newString(result);
+ return Encode(v4->newString(result));
}
-void DatePrototype::method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedObject O(scope, callData->thisObject.toObject(scope.engine));
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ Scope scope(v4);
+ ScopedObject O(scope, thisObject->toObject(v4));
+ if (v4->hasException)
+ return QV4::Encode::undefined();
ScopedValue tv(scope, RuntimeHelpers::toPrimitive(O, NUMBER_HINT));
if (tv->isNumber() && !std::isfinite(tv->toNumber()))
- RETURN_RESULT(Encode::null());
+ return Encode::null();
- ScopedString s(scope, scope.engine->newString(QStringLiteral("toISOString")));
+ ScopedString s(scope, v4->newString(QStringLiteral("toISOString")));
ScopedValue v(scope, O->get(s));
FunctionObject *toIso = v->as<FunctionObject>();
if (!toIso)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- ScopedCallData cData(scope);
- cData->thisObject = callData->thisObject;
- toIso->call(scope, cData);
+ return toIso->call(O, nullptr, 0);
}
void DatePrototype::timezoneUpdated()
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index b0373884dd..a4ab0a27ed 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -108,8 +108,8 @@ struct DateCtor: FunctionObject
{
V4_OBJECT2(DateCtor, FunctionObject)
- static void construct(const Managed *, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int);
};
struct DatePrototype: Object
@@ -118,57 +118,57 @@ struct DatePrototype: Object
void init(ExecutionEngine *engine, Object *ctor);
- static double getThisDate(Scope &scope, CallData *callData);
-
- static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_now(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static double getThisDate(ExecutionEngine *v4, const Value *thisObject);
+
+ static ReturnedValue method_parse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_UTC(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_now(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleDateString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleTimeString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCDay(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getTimezoneOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setTime(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCMilliseconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCSeconds(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCMinutes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCHours(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCDate(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCMonth(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setUTCFullYear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static void timezoneUpdated();
};
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 78cc95b98d..c57f39f61c 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -81,14 +81,6 @@
#include <QtCore/QTextStream>
#include <QDateTime>
-#ifdef V4_ENABLE_JIT
-#include "qv4isel_masm_p.h"
-#endif // V4_ENABLE_JIT
-
-#if QT_CONFIG(qml_interpreter)
-#include "qv4isel_moth_p.h"
-#endif
-
#if USE(PTHREADS)
# include <pthread.h>
#if !defined(Q_OS_INTEGRITY)
@@ -109,9 +101,9 @@ using namespace QV4;
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
-void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue throwTypeError(const BuiltinFunction *b, CallData *)
{
- scope.result = scope.engine->throwTypeError();
+ return b->engine()->throwTypeError();
}
@@ -129,7 +121,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const
qint32 ExecutionEngine::maxCallDepth = -1;
-ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
+ExecutionEngine::ExecutionEngine()
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
@@ -149,38 +141,15 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
bool ok = false;
maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
if (!ok || maxCallDepth <= 0) {
+#ifdef QT_NO_DEBUG
maxCallDepth = 1234;
- }
- }
- Q_ASSERT(maxCallDepth > 0);
-
- if (!factory) {
-#if QT_CONFIG(qml_interpreter)
- bool jitDisabled = true;
-
-#ifdef V4_ENABLE_JIT
- static const bool forceMoth = !qEnvironmentVariableIsEmpty("QV4_FORCE_INTERPRETER") ||
- !OSAllocator::canAllocateExecutableMemory();
- if (forceMoth) {
- factory = new Moth::ISelFactory;
- } else {
- factory = new JIT::ISelFactory<>;
- jitDisabled = false;
- }
-#else // !V4_ENABLE_JIT
- factory = new Moth::ISelFactory;
-#endif // V4_ENABLE_JIT
-
- if (jitDisabled) {
- qWarning("JIT is disabled for QML. Property bindings and animations will be "
- "very slow. Visit https://wiki.qt.io/V4 to learn about possible "
- "solutions for your platform.");
- }
#else
- factory = new JIT::ISelFactory<>;
+ // no (tail call) optimization is done, so there'll be a lot mare stack frames active
+ maxCallDepth = 200;
#endif
+ }
}
- iselFactory.reset(factory);
+ Q_ASSERT(maxCallDepth > 0);
// reserve space for the JS stack
// we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues
@@ -219,7 +188,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
- internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
jsStrings[String_Empty] = newIdentifier(QString());
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
@@ -273,6 +242,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
+ argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
+ argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
@@ -425,8 +396,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
//
// set up the global object
//
- rootContext()->d()->global.set(scope.engine, globalObject->d());
- rootContext()->d()->callData->thisObject = globalObject;
+ rootContext()->d()->activation.set(scope.engine, globalObject->d());
Q_ASSERT(globalObject->d()->vtable());
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
@@ -535,19 +505,11 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
void ExecutionEngine::initRootContext()
{
Scope scope(this);
- Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>(
- sizeof(GlobalContext::Data) + sizeof(CallData)));
- r->d_unchecked()->init(this);
- r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1);
- r->d()->callData->tag = quint32(Value::ValueTypeInternal::Integer);
- r->d()->callData->argc = 0;
- r->d()->callData->thisObject = globalObject;
- r->d()->callData->args[0] = Encode::undefined();
+ Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data)));
+ r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
+ r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
jsObjects[IntegerNull] = Encode((int)0);
-
- currentContext = static_cast<ExecutionContext *>(jsObjects + RootContext);
- current = currentContext->d();
}
InternalClass *ExecutionEngine::newClass(const InternalClass &other)
@@ -555,14 +517,6 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other)
return new (classPool) InternalClass(other);
}
-ExecutionContext *ExecutionEngine::pushGlobalContext()
-{
- pushContext(rootContext()->d());
-
- Q_ASSERT(current == rootContext()->d());
- return currentContext;
-}
-
InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
{
return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0);
@@ -633,6 +587,12 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng
// this doesn't require a write barrier, things will be ok, when the new array data gets inserted into
// the parent object
memcpy(&d->values.values, values, length*sizeof(Value));
+ for (int i = 0; i < length; ++i) {
+ if (values[i].isManaged()) {
+ d->needsMark = true;
+ break;
+ }
+ }
a->d()->arrayData.set(this, d);
a->setArrayLengthUnchecked(length);
}
@@ -685,9 +645,9 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
{
- bool global = (flags & IR::RegExp::RegExp_Global);
- bool ignoreCase = (flags & IR::RegExp::RegExp_IgnoreCase);
- bool multiline = (flags & IR::RegExp::RegExp_Multiline);
+ bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global);
+ bool ignoreCase = (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase);
+ bool multiline = (flags & QV4::CompiledData::RegExp::RegExp_Multiline);
Scope scope(this);
Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global));
@@ -760,11 +720,9 @@ Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o)
Heap::QmlContext *ExecutionEngine::qmlContext() const
{
- Heap::ExecutionContext *ctx = current;
-
- // get the correct context when we're within a builtin function
- if (ctx->type == Heap::ExecutionContext::Type_SimpleCallContext && !ctx->outer)
- ctx = parentContext(currentContext)->d();
+ if (!currentStackFrame)
+ return 0;
+ Heap::ExecutionContext *ctx = currentContext()->d();
if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer)
return 0;
@@ -785,7 +743,7 @@ QObject *ExecutionEngine::qmlScopeObject() const
if (!ctx)
return 0;
- return ctx->qml->scopeObject;
+ return ctx->qml()->scopeObject;
}
ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name)
@@ -815,57 +773,56 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const
if (!ctx)
return 0;
- return ctx->qml->context->contextData();
+ return ctx->qml()->context->contextData();
}
-QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
+QString CppStackFrame::source() const
{
- Scope scope(const_cast<ExecutionEngine *>(this));
- ScopedString name(scope);
- QVector<StackFrame> stack;
-
- ExecutionContext *c = currentContext;
- while (c && frameLimit) {
- QV4::Function *function = c->getFunction();
- if (function) {
- StackFrame frame;
- frame.source = function->sourceFile();
- name = function->name();
- frame.function = name->toQString();
-
- // line numbers can be negative for places where you can't set a real breakpoint
- frame.line = qAbs(c->d()->lineNumber);
- frame.column = -1;
-
- stack.append(frame);
- --frameLimit;
- }
- c = parentContext(c);
- }
-
- if (frameLimit && globalCode) {
- StackFrame frame;
- frame.source = globalCode->sourceFile();
- frame.function = globalCode->name()->toQString();
- frame.line = rootContext()->d()->lineNumber;
- frame.column = -1;
+ return v4Function->sourceFile();
+}
- stack.append(frame);
- }
- return stack;
+QString CppStackFrame::function() const
+{
+ return v4Function->name()->toQString();
}
-StackFrame ExecutionEngine::currentStackFrame() const
+int CppStackFrame::lineNumber() const
{
- StackFrame frame;
- frame.line = -1;
- frame.column = -1;
+ auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
+ return entry.codeOffset < offset;
+ };
+
+ const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
+ uint offset = instructionPointer;
+ const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
+ uint nLineNumbers = cf->nLineNumbers;
+ const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
+ return line->line;
+}
- QVector<StackFrame> trace = stackTrace(/*limit*/ 1);
- if (!trace.isEmpty())
- frame = trace.first();
+ReturnedValue CppStackFrame::thisObject() const {
+ return jsFrame->thisObject.asReturnedValue();
+}
- return frame;
+StackTrace ExecutionEngine::stackTrace(int frameLimit) const
+{
+ Scope scope(const_cast<ExecutionEngine *>(this));
+ ScopedString name(scope);
+ StackTrace stack;
+
+ CppStackFrame *f = currentStackFrame;
+ while (f && frameLimit) {
+ QV4::StackFrame frame;
+ frame.source = f->source();
+ frame.function = f->function();
+ frame.line = qAbs(f->lineNumber());
+ frame.column = -1;
+ stack.append(frame);
+ --frameLimit;
+ f = f->parent;
+ }
+
+ return stack;
}
/* Helper and "C" linkage exported function to format a GDBMI stacktrace for
@@ -908,14 +865,13 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
return src;
QUrl base;
- ExecutionContext *c = currentContext;
- while (c) {
- SimpleCallContext *callCtx = c->asSimpleCallContext();
- if (callCtx && callCtx->d()->v4Function) {
- base.setUrl(callCtx->d()->v4Function->sourceFile());
+ CppStackFrame *f = currentStackFrame;
+ while (f) {
+ if (f->v4Function) {
+ base.setUrl(f->v4Function->sourceFile());
break;
}
- c = parentContext(c);
+ f = f->parent;
}
if (base.isEmpty() && globalCode)
@@ -1589,9 +1545,13 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
return 0;
}
-void ExecutionEngine::failStackLimitCheck(Scope &scope)
+bool ExecutionEngine::canJIT()
{
- scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
+#ifdef V4_ENABLE_JIT
+ return true;
+#else
+ return false;
+#endif
}
// Converts a JS value to a meta-type.
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index e4ac51efed..e143fe2de2 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -51,7 +51,6 @@
//
#include "qv4global_p.h"
-#include "private/qv4isel_p.h"
#include "qv4managed_p.h"
#include "qv4context_p.h"
#include <private/qintrusivelist_p.h>
@@ -89,6 +88,32 @@ struct CompilationUnit;
struct InternalClass;
struct InternalClassPool;
+struct Q_QML_EXPORT CppStackFrame {
+ CppStackFrame *parent;
+ Function *v4Function;
+ CallData *jsFrame;
+ const Value *originalArguments;
+ int originalArgumentsCount;
+ int instructionPointer;
+
+ QString source() const;
+ QString function() const;
+ inline QV4::ExecutionContext *context() const {
+ return static_cast<ExecutionContext *>(&jsFrame->context);
+ }
+ int lineNumber() const;
+
+ 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;
+};
+
+
+
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
{
private:
@@ -100,7 +125,6 @@ private:
public:
ExecutableAllocator *executableAllocator;
ExecutableAllocator *regExpAllocator;
- QScopedPointer<EvalISelFactory> iselFactory;
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
@@ -183,7 +207,7 @@ public:
Value *jsObjects;
enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency
- GlobalContext *rootContext() const { return reinterpret_cast<GlobalContext *>(jsObjects + RootContext); }
+ ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); }
FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); }
FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); }
FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); }
@@ -342,7 +366,7 @@ public:
// bookkeeping.
MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects;
- ExecutionEngine(EvalISelFactory *iselFactory = 0);
+ ExecutionEngine();
~ExecutionEngine();
#if !QT_CONFIG(qml_debug)
@@ -359,11 +383,10 @@ public:
void setProfiler(Profiling::Profiler *profiler);
#endif // QT_CONFIG(qml_debug)
- ExecutionContext *pushGlobalContext();
- void pushContext(Heap::ExecutionContext *context);
- void pushContext(ExecutionContext *context);
- void popContext();
- ExecutionContext *parentContext(ExecutionContext *context) const;
+ void setCurrentContext(Heap::ExecutionContext *context);
+ ExecutionContext *currentContext() const {
+ return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
+ }
InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
@@ -413,7 +436,6 @@ public:
StackTrace stackTrace(int frameLimit = -1) const;
- StackFrame currentStackFrame() const;
QUrl resolvedUrl(const QString &file);
void requireArgumentsAccessors(int n);
@@ -424,8 +446,6 @@ public:
InternalClass *newClass(const InternalClass &other);
- // Exception handling
- Value *exceptionValue;
StackTrace exceptionStackTrace;
ReturnedValue throwError(const Value &value);
@@ -455,11 +475,11 @@ public:
bool metaTypeFromJS(const Value *value, int type, void *data);
QV4::ReturnedValue metaTypeToJS(int type, const void *data);
- bool checkStackLimits(Scope &scope);
+ bool checkStackLimits();
-private:
- void failStackLimitCheck(Scope &scope);
+ static bool canJIT();
+private:
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
@@ -477,71 +497,12 @@ struct NoThrowEngine;
#endif
-inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context)
-{
- Q_ASSERT(currentContext && context);
- Value *v = jsAlloca(2);
- v[0] = Encode(context);
- v[1] = Encode((int)(v - static_cast<Value *>(currentContext)));
- currentContext = static_cast<ExecutionContext *>(v);
- current = currentContext->d();
-}
-
-inline void ExecutionEngine::pushContext(ExecutionContext *context)
-{
- pushContext(context->d());
-}
-
-
-inline void ExecutionEngine::popContext()
-{
- Q_ASSERT(jsStackTop > currentContext);
- QV4::Value *offset = (currentContext + 1);
- Q_ASSERT(offset->isInteger());
- int o = offset->integerValue();
- Q_ASSERT(o);
- currentContext -= o;
- current = currentContext->d();
-}
-
-inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *context) const
-{
- Value *offset = static_cast<Value *>(context) + 1;
- Q_ASSERT(offset->isInteger());
- int o = offset->integerValue();
- return o ? context - o : 0;
-}
-
-inline
-void Heap::Base::mark(QV4::MarkStack *markStack)
-{
- Q_ASSERT(inUse());
- const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
- Chunk *c = h->chunk();
- size_t index = h - c->realBase();
- Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index));
- quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index);
- quintptr bit = Chunk::bitForIndex(index);
- if (!(*bitmap & bit)) {
- *bitmap |= bit;
- markStack->push(this);
- }
-}
-
-inline void Value::mark(MarkStack *markStack)
-{
- Heap::Base *o = heapObject();
- if (o)
- o->mark(markStack);
-}
-
-inline void Managed::mark(MarkStack *markStack)
+inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context)
{
- Q_ASSERT(m());
- m()->mark(markStack);
+ currentStackFrame->jsFrame->context = context;
}
-#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \
+#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
struct ExecutionEngineCallDepthRecorder
@@ -552,10 +513,10 @@ struct ExecutionEngineCallDepthRecorder
~ExecutionEngineCallDepthRecorder() { --ee->callDepth; }
};
-inline bool ExecutionEngine::checkStackLimits(Scope &scope)
+inline bool ExecutionEngine::checkStackLimits()
{
if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
- failStackLimitCheck(scope);
+ throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
return true;
}
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 88f9dfd85c..e0f5f3ffb1 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -57,31 +57,36 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct CppStackFrame;
+
// Base class for the execution engine
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(push, 1)
#endif
-struct EngineBase {
- Heap::ExecutionContext *current = 0;
+struct Q_QML_EXPORT EngineBase {
+
+ CppStackFrame *currentStackFrame = nullptr;
- Value *jsStackTop = 0;
+ Value *jsStackTop = nullptr;
quint8 hasException = false;
quint8 writeBarrierActive = false;
quint16 unused = 0;
#if QT_POINTER_SIZE == 8
quint8 padding[4];
#endif
- MemoryManager *memoryManager = 0;
+ MemoryManager *memoryManager = nullptr;
Runtime runtime;
qint32 callDepth = 0;
- Value *jsStackLimit = 0;
- Value *jsStackBase = 0;
+ Value *jsStackLimit = nullptr;
+ Value *jsStackBase = nullptr;
+
+ IdentifierTable *identifierTable = nullptr;
+ Object *globalObject = nullptr;
- ExecutionContext *currentContext = 0;
- IdentifierTable *identifierTable = 0;
- Object *globalObject = 0;
+ // Exception handling
+ Value *exceptionValue = nullptr;
enum {
Class_Empty,
@@ -90,7 +95,7 @@ struct EngineBase {
Class_SimpleArrayData,
Class_SparseArrayData,
Class_ExecutionContext,
- Class_SimpleCallContext,
+ Class_CallContext,
Class_Object,
Class_ArrayObject,
Class_FunctionObject,
@@ -115,8 +120,8 @@ struct EngineBase {
#endif
Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value);
-Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0);
-Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, currentStackFrame) == 0);
+Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, currentStackFrame) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE);
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index b3bd28e18b..90e158ba37 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -51,7 +51,6 @@
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
-#include <qv4jsir_p.h>
#include <qv4codegen_p.h>
#ifndef Q_OS_WIN
@@ -107,6 +106,7 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t)
void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t)
{
+ Q_UNUSED(fileName); // ####
Object::init();
errorType = t;
@@ -123,10 +123,9 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int
frame.column = column;
e->d()->stackTrace->prepend(frame);
- if (!e->d()->stackTrace->isEmpty()) {
- setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line));
- }
+ Q_ASSERT(!e->d()->stackTrace->isEmpty());
+ setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line));
if (!message.isUndefined())
setProperty(scope.engine, QV4::ErrorObject::Index_Message, message);
@@ -153,11 +152,12 @@ const char *ErrorObject::className(Heap::ErrorObject::ErrorType t)
Q_UNREACHABLE();
}
-void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ErrorObject::method_get_stack(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<ErrorObject> This(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const ErrorObject *This = thisObject->as<ErrorObject>();
if (!This)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
if (!This->d()->stack) {
QString trace;
for (int i = 0; i < This->d()->stackTrace->count(); ++i) {
@@ -168,9 +168,9 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa
if (frame.line >= 0)
trace += QLatin1Char(':') + QString::number(frame.line);
}
- This->d()->stack.set(scope.engine, scope.engine->newString(trace));
+ This->d()->stack.set(v4, v4->newString(trace));
}
- scope.result = This->d()->stack;
+ return This->d()->stack->asReturnedValue();
}
DEFINE_OBJECT_VTABLE(ErrorObject);
@@ -233,15 +233,15 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name)
Heap::FunctionObject::init(scope, name);
}
-void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<ErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue();
}
-void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- static_cast<const Object *>(that)->construct(scope, callData);
+ return f->callAsConstructor(argv, argc);
}
void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope)
@@ -249,10 +249,10 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("EvalError"));
}
-void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<EvalErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue();
}
void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope)
@@ -260,10 +260,10 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("RangeError"));
}
-void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<RangeErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue();
}
void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope)
@@ -271,10 +271,10 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError"));
}
-void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<ReferenceErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue();
}
void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope)
@@ -282,10 +282,10 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError"));
}
-void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<SyntaxErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue();
}
void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope)
@@ -293,10 +293,10 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("TypeError"));
}
-void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<TypeErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue();
}
void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope)
@@ -304,10 +304,10 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("URIError"));
}
-void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ScopedValue v(scope, callData->argument(0));
- scope.result = ErrorObject::create<URIErrorObject>(scope.engine, v);
+ Value v = argc ? *argv : Primitive::undefinedValue();
+ return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue();
}
void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t)
@@ -323,12 +323,14 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He
obj->defineDefaultProperty(engine->id_toString(), method_toString, 0);
}
-void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ErrorPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Object *o = callData->thisObject.as<Object>();
+ ExecutionEngine *v4 = b->engine();
+ const Object *o = thisObject->as<Object>();
if (!o)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
+ Scope scope(v4);
ScopedValue name(scope, o->get(scope.engine->id_name()));
QString qname;
if (name->isUndefined())
@@ -351,5 +353,5 @@ void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, Call
str = qname + QLatin1String(": ") + qmessage;
}
- scope.result = scope.engine->newString(str)->asReturnedValue();
+ return scope.engine->newString(str)->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index d556617b48..a5ee0eb886 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -67,7 +67,7 @@ namespace Heap {
Member(class, Pointer, String *, stack)
DECLARE_HEAP_OBJECT(ErrorObject, Object) {
- DECLARE_MARK_TABLE(ErrorObject);
+ DECLARE_MARKOBJECTS(ErrorObject);
enum ErrorType {
Error,
EvalError,
@@ -175,7 +175,7 @@ struct ErrorObject: Object {
static const char *className(Heap::ErrorObject::ErrorType t);
- static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_stack(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
template<>
@@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject
{
V4_OBJECT2(ErrorCtor, FunctionObject)
- static void construct(const Managed *, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct EvalErrorCtor: ErrorCtor
{
V4_OBJECT2(EvalErrorCtor, ErrorCtor)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
struct RangeErrorCtor: ErrorCtor
{
V4_OBJECT2(RangeErrorCtor, ErrorCtor)
- static void construct(const Managed *, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
struct ReferenceErrorCtor: ErrorCtor
{
V4_OBJECT2(ReferenceErrorCtor, ErrorCtor)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
struct SyntaxErrorCtor: ErrorCtor
{
V4_OBJECT2(SyntaxErrorCtor, ErrorCtor)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
struct TypeErrorCtor: ErrorCtor
{
V4_OBJECT2(TypeErrorCtor, ErrorCtor)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
struct URIErrorCtor: ErrorCtor
{
V4_OBJECT2(URIErrorCtor, ErrorCtor)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
};
@@ -286,7 +286,7 @@ struct ErrorPrototype : ErrorObject
void init(ExecutionEngine *engine, Object *ctor) { init(engine, ctor, this, Heap::ErrorObject::Error); }
static void init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct EvalErrorPrototype : ErrorObject
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 4b88d85e68..83e861138b 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qv4function_p.h"
+#include "qv4functionobject_p.h"
#include "qv4managed_p.h"
#include "qv4string_p.h"
#include "qv4value_p.h"
@@ -45,76 +46,83 @@
#include "qv4lookup_p.h"
#include <private/qv4mm_p.h>
#include <private/qv4identifiertable_p.h>
+#include <assembler/MacroAssemblerCodeRef.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function,
- ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *))
+Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr)
: compiledFunction(function)
, compilationUnit(unit)
, code(codePtr)
- , codeData(0)
+ , codeData(function->code())
+ , jittedCode(nullptr)
+ , codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
- internalClass = engine->internalClasses[EngineBase::Class_Empty];
- const quint32_le *formalsIndices = compiledFunction->formalsTable();
- // iterate backwards, so we get the right ordering for duplicate names
- Scope scope(engine);
- ScopedString arg(scope);
- for (int i = static_cast<int>(compiledFunction->nFormals - 1); i >= 0; --i) {
- arg = compilationUnit->runtimeStrings[formalsIndices[i]];
- while (1) {
- InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable);
- if (newClass != internalClass) {
- internalClass = newClass;
- break;
- }
- // duplicate arguments, need some trick to store them
- MemoryManager *mm = engine->memoryManager;
- arg = mm->alloc<String>(arg->d(), engine->newString(QString(0xfffe)));
- }
- }
- nFormals = compiledFunction->nFormals;
+ Q_UNUSED(engine);
+
+ internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ // first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
- canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall;
+ const quint32_le *formalsIndices = compiledFunction->formalsTable();
+ for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
+ internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+
+ nFormals = compiledFunction->nFormals;
}
Function::~Function()
{
+ delete codeRef;
}
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
{
- internalClass = engine->internalClasses[EngineBase::Class_Empty];
+ QStringList parameterNames;
- // iterate backwards, so we get the right ordering for duplicate names
- Scope scope(engine);
- ScopedString arg(scope);
- for (int i = parameters.size() - 1; i >= 0; --i) {
- arg = engine->newString(QString::fromUtf8(parameters.at(i)));
- while (1) {
- InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable);
- if (newClass != internalClass) {
- internalClass = newClass;
+ // Resolve duplicate parameter names:
+ for (int i = 0, ei = parameters.count(); i != ei; ++i) {
+ const QByteArray &param = parameters.at(i);
+ int duplicate = -1;
+
+ for (int j = i - 1; j >= 0; --j) {
+ const QByteArray &prevParam = parameters.at(j);
+ if (param == prevParam) {
+ duplicate = j;
break;
}
- // duplicate arguments, need some trick to store them
- arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe)));
}
+
+ if (duplicate == -1) {
+ parameterNames.append(QString::fromUtf8(param));
+ } else {
+ const QString &dup = parameterNames[duplicate];
+ parameterNames.append(dup);
+ parameterNames[duplicate] =
+ QString(0xfffe) + QString::number(duplicate) + dup;
+ }
+
+ }
+
+ internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+
+ Scope scope(engine);
+ ScopedString arg(scope);
+ for (const QString &parameterName : parameterNames) {
+ arg = engine->newString(parameterName);
+ internalClass = internalClass->addMember(arg, Attr_NotConfigurable);
}
nFormals = parameters.size();
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
-
- canUseSimpleCall = false;
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index b11c8af94a..0e61be5115 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -54,6 +54,11 @@
#include <private/qqmlglobal_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4context_p.h>
+#include <private/qv4vme_moth_p.h>
+
+namespace JSC {
+class MacroAssemblerCodeRef;
+}
QT_BEGIN_NAMESPACE
@@ -63,17 +68,24 @@ struct Q_QML_EXPORT Function {
const CompiledData::Function *compiledFunction;
CompiledData::CompilationUnit *compilationUnit;
- ReturnedValue (*code)(ExecutionEngine *, const uchar *);
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
+ return Moth::VME::exec(this, thisObject, argv, argc, context);
+ }
+
+ typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
+ Code code;
const uchar *codeData;
+ typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
+ JittedCode jittedCode;
+ JSC::MacroAssemblerCodeRef *codeRef;
+
// first nArguments names in internalClass are the actual arguments
InternalClass *internalClass;
uint nFormals;
bool hasQmlDependencies;
- bool canUseSimpleCall;
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function,
- ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *));
+ Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr);
~Function();
// used when dynamically assigning signal handlers (QQmlConnection)
@@ -86,24 +98,13 @@ struct Q_QML_EXPORT Function {
inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; }
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
- inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; }
-
- inline bool canUseSimpleFunction() const { return canUseSimpleCall; }
QQmlSourceLocation sourceLocation() const
{
return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
}
-
};
-
-inline unsigned int Heap::SimpleCallContext::formalParameterCount() const
-{
- return v4Function ? v4Function->nFormals : 0;
-}
-
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 45fdde98f7..4293d43791 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -38,8 +38,6 @@
****************************************************************************/
#include "qv4object_p.h"
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
#include "qv4function_p.h"
@@ -55,9 +53,10 @@
#include <private/qqmljsast_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qqmlengine_p.h>
-#include <qv4codegen_p.h>
+#include <qv4runtimecodegen_p.h>
#include "private/qlocale_tools_p.h"
#include "private/qqmlbuiltinfunctions_p.h"
+#include <private/qv4jscall_p.h>
#include <QtCore/QDebug>
#include <algorithm>
@@ -69,10 +68,25 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(FunctionObject);
-Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == Heap::Object::markTable);
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
+ ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc))
+{
+ jsCall = code;
+ jsConstruct = QV4::FunctionObject::callAsConstructor;
+
+ Object::init();
+ function = nullptr;
+ this->scope.set(scope->engine(), scope->d());
+ Scope s(scope->engine());
+ ScopedFunctionObject f(s, this);
+ f->init(name, false);
+}
void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto)
{
+ jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
+ jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+
Object::init();
function = nullptr;
this->scope.set(scope->engine(), scope->d());
@@ -83,6 +97,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto)
{
+ jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
+ jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+
Object::init();
this->function = function;
function->compilationUnit->addref();
@@ -102,6 +119,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam
void Heap::FunctionObject::init()
{
+ jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
+ jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+
Object::init();
function = nullptr;
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
@@ -141,14 +161,14 @@ ReturnedValue FunctionObject::name() const
return get(scope()->internalClass->engine->id_name());
}
-void FunctionObject::construct(const Managed *that, Scope &scope, CallData *)
+ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int)
{
- scope.result = static_cast<const FunctionObject *>(that)->engine()->throwTypeError();
+ return f->engine()->throwTypeError();
}
-void FunctionObject::call(const Managed *, Scope &scope, CallData *)
+ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int)
{
- scope.result = Encode::undefined();
+ return Encode::undefined();
}
Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
@@ -179,24 +199,22 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
}
// 15.3.2
-void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that));
+ Scope scope(f->engine());
QString arguments;
QString body;
- if (callData->argc > 0) {
- for (int i = 0; i < callData->argc - 1; ++i) {
+ if (argc > 0) {
+ for (int i = 0, ei = argc - 1; i < ei; ++i) {
if (i)
arguments += QLatin1String(", ");
- arguments += callData->args[i].toQString();
+ arguments += argv[i].toQString();
}
- body = callData->args[callData->argc - 1].toQString();
- }
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
+ body = argv[argc - 1].toQString();
}
+ if (scope.engine->hasException)
+ return Encode::undefined();
QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}');
@@ -207,36 +225,30 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa
const bool parsed = parser.parseExpression();
- if (!parsed) {
- scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error"));
- return;
- }
+ if (!parsed)
+ return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
- using namespace QQmlJS::AST;
- FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode());
- if (!fe) {
- scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error"));
- return;
- }
+ QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode());
+ if (!fe)
+ return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
- IR::Module module(scope.engine->debugger() != 0);
+ Compiler::Module module(scope.engine->debugger() != 0);
- QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode());
+ Compiler::JSUnitGenerator jsGenerator(&module);
+ RuntimeCodegen cg(scope.engine, &jsGenerator, false);
cg.generateFromFunctionExpression(QString(), function, fe, &module);
- Compiler::JSUnitGenerator jsGenerator(&module);
- QScopedPointer<EvalInstructionSelection> isel(scope.engine->iselFactory->create(QQmlEnginePrivate::get(scope.engine), scope.engine->executableAllocator, &module, &jsGenerator));
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = isel->compile();
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit();
Function *vmf = compilationUnit->linkToEngine(scope.engine);
ExecutionContext *global = scope.engine->rootContext();
- scope.result = FunctionObject::createScriptFunction(global, vmf);
+ return Encode(FunctionObject::createScriptFunction(global, vmf));
}
// 15.3.1: This is equivalent to new Function(...)
-void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
DEFINE_OBJECT_VTABLE(FunctionPrototype);
@@ -263,152 +275,117 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor)
}
-void FunctionPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- FunctionObject *fun = callData->thisObject.as<FunctionObject>();
+ ExecutionEngine *v4 = b->engine();
+ const FunctionObject *fun = thisObject->as<FunctionObject>();
if (!fun)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = scope.engine->newString(QStringLiteral("function() { [code] }"));
+ return Encode(v4->newString(QStringLiteral("function() { [code] }")));
}
-void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- FunctionObject *o = callData->thisObject.as<FunctionObject>();
- if (!o)
- THROW_TYPE_ERROR();
+ ExecutionEngine *v4 = b->engine();
+ const FunctionObject *f = thisObject->as<FunctionObject>();
+ if (!f)
+ return v4->throwTypeError();
+ thisObject = argc ? argv : nullptr;
+ if (argc < 2 || argv[1].isNullOrUndefined())
+ return f->call(thisObject, argv, 0);
- ScopedValue arg(scope, callData->argument(1));
+ Object *arr = argv[1].objectValue();
+ if (!arr)
+ return v4->throwTypeError();
- ScopedObject arr(scope, arg);
-
- quint32 len;
- if (!arr) {
- len = 0;
- if (!arg->isNullOrUndefined())
- THROW_TYPE_ERROR();
- } else {
- len = arr->getLength();
- }
-
- ScopedCallData cData(scope, len);
+ uint len = arr->getLength();
+ Scope scope(v4);
+ Value *arguments = v4->jsAlloca(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>();
- int l = qMin(len, (uint)a->d()->context->callData->argc);
- memcpy(cData->args, a->d()->context->callData->args, l*sizeof(Value));
+ int l = qMin(len, (uint)a->d()->context->argc());
+ memcpy(arguments, a->d()->context->args(), l*sizeof(Value));
for (quint32 i = l; i < len; ++i)
- cData->args[i] = Primitive::undefinedValue();
+ arguments[i] = Primitive::undefinedValue();
} else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) {
auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData());
uint alen = sad ? sad->values.size : 0;
if (alen > len)
alen = len;
for (uint i = 0; i < alen; ++i)
- cData->args[i] = sad->data(i);
+ arguments[i] = sad->data(i);
for (quint32 i = alen; i < len; ++i)
- cData->args[i] = Primitive::undefinedValue();
+ arguments[i] = Primitive::undefinedValue();
} else {
for (quint32 i = 0; i < len; ++i)
- cData->args[i] = arr->getIndexed(i);
+ arguments[i] = arr->getIndexed(i);
}
}
- cData->thisObject = callData->argument(0);
- o->call(scope, cData);
+ return f->call(thisObject, arguments, len);
}
-void FunctionPrototype::method_call(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- FunctionObject *o = callData->thisObject.as<FunctionObject>();
- if (!o)
- THROW_TYPE_ERROR();
+ if (!thisObject->isFunctionObject())
+ return b->engine()->throwTypeError();
- ScopedCallData cData(scope, callData->argc ? callData->argc - 1 : 0);
- if (callData->argc) {
- for (int i = 1; i < callData->argc; ++i)
- cData->args[i - 1] = callData->args[i];
- }
- cData->thisObject = callData->argument(0);
+ const FunctionObject *f = static_cast<const FunctionObject *>(thisObject);
- o->call(scope, cData);
+ thisObject = argc ? argv : nullptr;
+ if (argc) {
+ ++argv;
+ --argc;
+ }
+ return f->call(thisObject, argv, argc);
}
-void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- FunctionObject *target = callData->thisObject.as<FunctionObject>();
+ QV4::Scope scope(b);
+ ScopedFunctionObject target(scope, thisObject);
if (!target)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
- ScopedValue boundThis(scope, callData->argument(0));
+ ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue());
Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0);
- if (callData->argc > 1) {
- boundArgs = MemberData::allocate(scope.engine, callData->argc - 1);
- boundArgs->d()->values.size = callData->argc - 1;
- for (uint i = 0; i < static_cast<uint>(callData->argc - 1); ++i)
- boundArgs->set(scope.engine, i, callData->args[i + 1]);
+ if (argc > 1) {
+ boundArgs = MemberData::allocate(scope.engine, argc - 1);
+ boundArgs->d()->values.size = argc - 1;
+ for (uint i = 0, ei = static_cast<uint>(argc - 1); i < ei; ++i)
+ boundArgs->set(scope.engine, i, argv[i + 1]);
}
ExecutionContext *global = scope.engine->rootContext();
- scope.result = BoundFunction::create(global, target, boundThis, boundArgs);
+ return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue();
}
DEFINE_OBJECT_VTABLE(ScriptFunction);
-void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
{
- ExecutionEngine *v4 = scope.engine;
- if (Q_UNLIKELY(v4->hasException)) {
- scope.result = Encode::undefined();
- return;
- }
- CHECK_STACK_LIMITS(v4, scope);
+ ExecutionEngine *v4 = fo->engine();
+ const ScriptFunction *f = static_cast<const ScriptFunction *>(fo);
- ExecutionContextSaver ctxSaver(scope);
+ Scope scope(v4);
+ InternalClass *ic = f->classForConstructor();
+ ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic));
- Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that));
+ ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc);
- InternalClass *ic = f->classForConstructor();
- ScopedObject proto(scope, ic->prototype);
- ScopedObject obj(scope, v4->newObject(ic, proto));
- callData->thisObject = obj.asReturnedValue();
-
- QV4::Function *v4Function = f->function();
- Q_ASSERT(v4Function);
-
- ScopedContext c(scope, f->scope());
- if (v4Function->canUseSimpleCall)
- c->simpleCall(scope, callData, v4Function);
- else
- c->call(scope, callData, v4Function, f);
-
- if (Q_UNLIKELY(v4->hasException)) {
- scope.result = Encode::undefined();
- } else if (!scope.result.isObject()) {
- scope.result = obj.asReturnedValue();
- }
+ if (Q_UNLIKELY(v4->hasException))
+ return Encode::undefined();
+ else if (!Value::fromReturnedValue(result).isObject())
+ return thisObject->asReturnedValue();
+ return result;
}
-void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
{
- ExecutionEngine *v4 = scope.engine;
- if (Q_UNLIKELY(v4->hasException)) {
- scope.result = Encode::undefined();
- return;
- }
- CHECK_STACK_LIMITS(v4, scope);
-
- Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that));
-
- QV4::Function *v4Function = f->function();
- Q_ASSERT(v4Function);
-
- ScopedContext c(scope, f->scope());
- if (v4Function->canUseSimpleCall)
- c->simpleCall(scope, callData, v4Function);
- else
- c->call(scope, callData, v4Function, f);
+ return Moth::VME::exec(fo, thisObject, argv, argc);
}
void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function)
@@ -429,7 +406,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length);
setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount()));
- if (scope->d()->strictMode) {
+ if (function->isStrict()) {
ScopedProperty pd(s);
pd->value = s.engine->thrower();
pd->set = s.engine->thrower();
@@ -455,49 +432,23 @@ InternalClass *ScriptFunction::classForConstructor() const
DEFINE_OBJECT_VTABLE(BuiltinFunction);
-void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *))
+void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *))
{
Heap::FunctionObject::init(scope, name);
this->code = code;
}
-void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
-{
- scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
-}
-
-void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue BuiltinFunction::callAsConstructor(const QV4::FunctionObject *f, const Value *, int)
{
- const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that);
- ExecutionEngine *v4 = scope.engine;
- if (v4->hasException) {
- scope.result = Encode::undefined();
- return;
- }
- f->d()->code(f, scope, callData);
+ return f->engine()->throwTypeError();
}
-
-void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue BuiltinFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
{
- const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that);
- ExecutionEngine *v4 = scope.engine;
- if (v4->hasException) {
- scope.result = Encode::undefined();
- return;
- }
- CHECK_STACK_LIMITS(v4, scope);
-
- ExecutionContextSaver ctxSaver(scope);
-
- SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext();
- ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
- ctx->callData = callData;
- v4->pushContext(ctx);
- Q_ASSERT(v4->current == ctx);
-
- scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index);
- v4->memoryManager->freeSimpleCallContext();
+ const BuiltinFunction *f = static_cast<const BuiltinFunction *>(fo);
+ Scope scope(f->engine());
+ JSCallData callData(scope, argc, argv, thisObject);
+ return f->d()->code(f, callData.callData());
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);
@@ -530,43 +481,43 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject
f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
}
-void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd)
+ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc)
{
- const BoundFunction *f = static_cast<const BoundFunction *>(that);
- if (scope.hasException()) {
- scope.result = Encode::undefined();
- return;
- }
+ const BoundFunction *f = static_cast<const BoundFunction *>(fo);
+ Scope scope(f->engine());
+
+ if (scope.hasException())
+ return Encode::undefined();
Scoped<MemberData> boundArgs(scope, f->boundArgs());
- ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc);
- callData->thisObject = f->boundThis();
- Value *argp = callData->args;
+ ScopedFunctionObject target(scope, f->target());
+ JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc);
+ *jsCallData->thisObject = f->boundThis();
+ Value *argp = jsCallData->args;
if (boundArgs) {
memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value));
argp += boundArgs->size();
}
- memcpy(argp, dd->args, dd->argc*sizeof(Value));
- ScopedFunctionObject t(scope, f->target());
- t->call(scope, callData);
+ memcpy(argp, argv, argc*sizeof(Value));
+ return target->call(jsCallData);
}
-void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd)
+ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
{
- const BoundFunction *f = static_cast<const BoundFunction *>(that);
- if (scope.hasException()) {
- scope.result = Encode::undefined();
- return;
- }
+ const BoundFunction *f = static_cast<const BoundFunction *>(fo);
+ Scope scope(f->engine());
+
+ if (scope.hasException())
+ return Encode::undefined();
Scoped<MemberData> boundArgs(scope, f->boundArgs());
- ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc);
- Value *argp = callData->args;
+ ScopedFunctionObject target(scope, f->target());
+ JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc);
+ Value *argp = jsCallData->args;
if (boundArgs) {
memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value));
argp += boundArgs->size();
}
- memcpy(argp, dd->args, dd->argc*sizeof(Value));
- ScopedFunctionObject t(scope, f->target());
- t->construct(scope, callData);
+ memcpy(argp, argv, argc*sizeof(Value));
+ return target->callAsConstructor(jsCallData);
}
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 6ce5734b6d..d61006a6b0 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -62,20 +62,29 @@ struct QQmlSourceLocation;
namespace QV4 {
struct BuiltinFunction;
+struct IndexedBuiltinFunction;
+struct JSCallData;
+
+typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc);
namespace Heap {
+
#define FunctionObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, scope) \
- Member(class, NoMark, Function *, function)
+ Member(class, NoMark, Function *, function) \
+ Member(class, NoMark, jsCallFunction, jsCall) \
+ Member(class, NoMark, jsConstructFunction, jsConstruct)
DECLARE_HEAP_OBJECT(FunctionObject, Object) {
- DECLARE_MARK_TABLE(FunctionObject);
+ DECLARE_MARKOBJECTS(FunctionObject);
enum {
Index_Prototype = 0,
Index_ProtoConstructor = 0
};
+ void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc));
void init(QV4::ExecutionContext *scope, QV4::String *name = 0, bool createProto = false);
void init(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false);
void init(QV4::ExecutionContext *scope, const QString &name, bool createProto = false);
@@ -96,19 +105,13 @@ struct FunctionPrototype : FunctionObject {
void init();
};
-struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject {
- void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *));
- ReturnedValue (*code)(QV4::CallContext *);
-};
-
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
- void init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *));
- void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *);
+ void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *));
+ ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *);
};
-struct IndexedBuiltinFunction : FunctionObject {
- inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index));
- ReturnedValue (*code)(QV4::CallContext *, uint index);
+struct IndexedBuiltinFunction : BuiltinFunction {
+ inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *));
uint index;
};
@@ -128,7 +131,7 @@ struct ScriptFunction : FunctionObject {
Member(class, Pointer, MemberData *, boundArgs)
DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) {
- DECLARE_MARK_TABLE(BoundFunction);
+ DECLARE_MARKOBJECTS(BoundFunction);
void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs);
};
@@ -144,6 +147,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
V4_INTERNALCLASS(FunctionObject)
V4_PROTOTYPE(functionPrototype)
V4_NEEDS_DESTROY
+ enum { NInlineProperties = 1 };
Heap::ExecutionContext *scope() const { return d()->scope; }
Function *function() const { return d()->function; }
@@ -154,10 +158,16 @@ struct Q_QML_EXPORT FunctionObject: Object {
void init(String *name, bool createProto);
- using Object::construct;
- using Object::call;
- static void construct(const Managed *that, Scope &scope, CallData *);
- static void call(const Managed *that, Scope &scope, CallData *d);
+ inline ReturnedValue callAsConstructor(const JSCallData &data) const;
+ ReturnedValue callAsConstructor(const Value *argv, int argc) const {
+ return d()->jsConstruct(this, argv, argc);
+ }
+ inline ReturnedValue call(const JSCallData &data) const;
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const {
+ return d()->jsCall(this, thisObject, argv, argc);
+ }
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function);
@@ -178,8 +188,8 @@ struct FunctionCtor: FunctionObject
{
V4_OBJECT2(FunctionCtor, FunctionObject)
- static void construct(const Managed *that, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct FunctionPrototype: FunctionObject
@@ -188,52 +198,51 @@ struct FunctionPrototype: FunctionObject
void init(ExecutionEngine *engine, Object *ctor);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_apply(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_call(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
V4_OBJECT2(BuiltinFunction, FunctionObject)
V4_INTERNALCLASS(BuiltinFunction)
- static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *))
+ static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *))
{
return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code);
}
- static void construct(const Managed *, Scope &scope, CallData *);
- static void call(const Managed *that, Scope &scope, CallData *callData);
-};
-
-struct IndexedBuiltinFunction: FunctionObject
-{
- V4_OBJECT2(IndexedBuiltinFunction, FunctionObject)
-
- static void construct(const Managed *m, Scope &scope, CallData *)
+ static Heap::FunctionObject *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc))
{
- scope.result = static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError();
+ return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code);
}
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct IndexedBuiltinFunction: BuiltinFunction
+{
+ V4_OBJECT2(IndexedBuiltinFunction, BuiltinFunction)
};
void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index,
- ReturnedValue (*code)(QV4::CallContext *ctx, uint index))
+ ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *))
{
Heap::FunctionObject::init(scope);
- this->index = index;
this->code = code;
+ this->index = index;
}
struct ScriptFunction : FunctionObject {
V4_OBJECT2(ScriptFunction, FunctionObject)
V4_INTERNALCLASS(ScriptFunction)
+ enum { NInlineProperties = 3 };
- static void construct(const Managed *, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
InternalClass *classForConstructor() const;
};
@@ -251,8 +260,8 @@ struct BoundFunction: FunctionObject {
Value boundThis() const { return d()->boundThis; }
Heap::MemberData *boundArgs() const { return d()->boundArgs; }
- static void construct(const Managed *, Scope &scope, CallData *d);
- static void call(const Managed *that, Scope &scope, CallData *dd);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 8769519a59..090a164ef6 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -107,8 +107,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
# if defined(Q_OS_LINUX)
# define V4_ENABLE_JIT
# endif
-#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX)
-# define V4_ENABLE_JIT
+//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX)
+//# define V4_ENABLE_JIT
#endif
// Black list some platforms
@@ -147,6 +147,12 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+namespace Compiler {
+ struct Module;
+ struct Context;
+ struct JSUnitGenerator;
+}
+
namespace Heap {
struct Base;
struct MemberData;
@@ -157,7 +163,6 @@ namespace Heap {
struct ObjectPrototype;
struct ExecutionContext;
- struct GlobalContext;
struct CallContext;
struct ScriptFunction;
@@ -182,12 +187,12 @@ namespace Heap {
}
class MemoryManager;
+class ExecutableAllocator;
struct String;
struct Object;
struct ObjectPrototype;
struct ObjectIterator;
struct ExecutionContext;
-struct GlobalContext;
struct CallContext;
struct ScriptFunction;
struct InternalClass;
@@ -243,12 +248,6 @@ struct IdentifierTable;
class RegExpCache;
class MultiplyWrappedQObjectMap;
-namespace Global {
- enum {
- ReservedArgumentCount = 6
- };
-}
-
enum PropertyFlag {
Attr_Data = 0,
Attr_Accessor = 0x1,
@@ -349,11 +348,11 @@ struct PropertyAttributes
}
};
-struct StackFrame {
+struct Q_QML_EXPORT StackFrame {
QString source;
QString function;
- int line;
- int column;
+ int line = -1;
+ int column = -1;
};
typedef QVector<StackFrame> StackTrace;
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index 0916e8e110..3214a716e8 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -47,12 +47,12 @@
#include "qv4script_p.h"
#include "qv4scopedvalue_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
-#include <qv4jsir_p.h>
#include <qv4codegen_p.h>
#include "private/qlocale_tools_p.h"
#include "private/qtools_p.h"
@@ -338,72 +338,56 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope)
f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1));
}
-void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const
+ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const
{
- if (callData->argc < 1) {
- scope.result = Encode::undefined();
- return;
- }
+ if (argc < 1)
+ return Encode::undefined();
ExecutionEngine *v4 = engine();
- ExecutionContextSaver ctxSaver(scope);
+ bool isStrict = v4->currentStackFrame->v4Function->isStrict();
- ExecutionContext *currentContext = v4->currentContext;
- ExecutionContext *ctx = currentContext;
+ Scope scope(v4);
+ ScopedContext ctx(scope, v4->currentContext());
if (!directCall) {
- // the context for eval should be the global scope, so we fake a root
- // context
- ctx = v4->pushGlobalContext();
+ // the context for eval should be the global scope
+ ctx = v4->rootContext();
}
- String *scode = callData->args[0].stringValue();
- if (!scode) {
- scope.result = callData->args[0].asReturnedValue();
- return;
- }
+ String *scode = argv[0].stringValue();
+ if (!scode)
+ return argv[0].asReturnedValue();
const QString code = scode->toQString();
- bool inheritContext = !ctx->d()->strictMode;
+ bool inheritContext = !isStrict;
- Script script(ctx, code, QStringLiteral("eval code"));
- script.strictMode = (directCall && currentContext->d()->strictMode);
+ Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code"));
+ script.strictMode = (directCall && isStrict);
script.inheritContext = inheritContext;
script.parse();
- if (v4->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (v4->hasException)
+ return Encode::undefined();
Function *function = script.function();
- if (!function) {
- scope.result = Encode::undefined();
- return;
- }
+ if (!function)
+ return Encode::undefined();
- if (function->isStrict() || (ctx->d()->strictMode)) {
+ if (function->isStrict() || isStrict) {
ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function));
- ScopedCallData callData(scope, 0);
- callData->thisObject = ctx->thisObject();
- e->call(scope, callData);
- return;
+ ScopedValue thisObject(scope, directCall ? scope.engine->currentStackFrame->thisObject() : scope.engine->globalObject->asReturnedValue());
+ return e->call(thisObject, 0, 0);
}
- ContextStateSaver stateSaver(scope, ctx);
-
- // set the correct strict mode flag on the context
- ctx->d()->strictMode = false;
- ctx->d()->compilationUnit = function->compilationUnit;
- ctx->d()->constantTable = function->compilationUnit->constants;
+ ScopedValue thisObject(scope, scope.engine->currentStackFrame->thisObject());
- scope.result = Q_V4_PROFILE(ctx->engine(), function);
+ return function->call(thisObject, 0, 0, ctx);
}
-void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
// indirect call
- static_cast<const EvalFunction *>(that)->evalCall(scope, callData, false);
+ return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false);
}
@@ -424,10 +408,11 @@ static inline int toInt(const QChar &qc, int R)
}
// parseInt [15.1.2.2]
-void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedValue inputString(scope, callData->argument(0));
- ScopedValue radix(scope, callData->argument(1));
+ Scope scope(b);
+ ScopedValue inputString(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue radix(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
int R = radix->isUndefined() ? 0 : radix->toInt32();
// [15.1.2.2] step by step:
@@ -504,10 +489,11 @@ void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, Cal
}
// parseFloat [15.1.2.3]
-void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_parseFloat(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
+ Scope scope(b);
// [15.1.2.3] step by step:
- ScopedString inputString(scope, callData->argument(0), ScopedString::Convert);
+ ScopedString inputString(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
CHECK_EXCEPTION();
QString trimmed = inputString->toQString().trimmed(); // 2
@@ -530,115 +516,125 @@ void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, C
}
/// isNaN [15.1.2.4]
-void GlobalFunctions::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
+ if (!argc)
// undefined gets converted to NaN
RETURN_RESULT(Encode(true));
- if (callData->args[0].integerCompatible())
+ if (argv[0].integerCompatible())
RETURN_RESULT(Encode(false));
- double d = callData->args[0].toNumber();
+ double d = argv[0].toNumber();
RETURN_RESULT(Encode((bool)std::isnan(d)));
}
/// isFinite [15.1.2.5]
-void GlobalFunctions::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
+ if (!argc)
// undefined gets converted to NaN
RETURN_RESULT(Encode(false));
- if (callData->args[0].integerCompatible())
+ if (argv[0].integerCompatible())
RETURN_RESULT(Encode(true));
- double d = callData->args[0].toNumber();
+ double d = argv[0].toNumber();
RETURN_RESULT(Encode((bool)std::isfinite(d)));
}
/// decodeURI [15.1.3.1]
-void GlobalFunctions::method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_decodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (callData->argc == 0)
+ if (argc == 0)
RETURN_UNDEFINED();
- QString uriString = callData->args[0].toQString();
+ ExecutionEngine *v4 = b->engine();
+ QString uriString = argv[0].toQString();
bool ok;
QString out = decode(uriString, DecodeNonReserved, &ok);
if (!ok) {
+ Scope scope(v4);
ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
RETURN_RESULT(scope.engine->throwURIError(s));
}
- RETURN_RESULT(scope.engine->newString(out));
+ RETURN_RESULT(v4->newString(out));
}
/// decodeURIComponent [15.1.3.2]
-void GlobalFunctions::method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_decodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (callData->argc == 0)
+ if (argc == 0)
RETURN_UNDEFINED();
- QString uriString = callData->args[0].toQString();
+ ExecutionEngine *v4 = b->engine();
+ QString uriString = argv[0].toQString();
bool ok;
QString out = decode(uriString, DecodeAll, &ok);
if (!ok) {
+ Scope scope(v4);
ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
RETURN_RESULT(scope.engine->throwURIError(s));
}
- RETURN_RESULT(scope.engine->newString(out));
+ RETURN_RESULT(v4->newString(out));
}
/// encodeURI [15.1.3.3]
-void GlobalFunctions::method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_encodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (callData->argc == 0)
+ if (argc == 0)
RETURN_UNDEFINED();
- QString uriString = callData->args[0].toQString();
+ ExecutionEngine *v4 = b->engine();
+ QString uriString = argv[0].toQString();
bool ok;
QString out = encode(uriString, uriUnescapedReserved, &ok);
if (!ok) {
+ Scope scope(v4);
ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
RETURN_RESULT(scope.engine->throwURIError(s));
}
- RETURN_RESULT(scope.engine->newString(out));
+ RETURN_RESULT(v4->newString(out));
}
/// encodeURIComponent [15.1.3.4]
-void GlobalFunctions::method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_encodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (callData->argc == 0)
+ if (argc == 0)
RETURN_UNDEFINED();
- QString uriString = callData->args[0].toQString();
+ ExecutionEngine *v4 = b->engine();
+ QString uriString = argv[0].toQString();
bool ok;
QString out = encode(uriString, uriUnescaped, &ok);
if (!ok) {
+ Scope scope(v4);
ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
RETURN_RESULT(scope.engine->throwURIError(s));
}
- RETURN_RESULT(scope.engine->newString(out));
+ RETURN_RESULT(v4->newString(out));
}
-void GlobalFunctions::method_escape(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_escape(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
- RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined")));
+ ExecutionEngine *v4 = b->engine();
+ if (!argc)
+ RETURN_RESULT(v4->newString(QStringLiteral("undefined")));
- QString str = callData->args[0].toQString();
- RETURN_RESULT(scope.engine->newString(escape(str)));
+ QString str = argv[0].toQString();
+ RETURN_RESULT(v4->newString(escape(str)));
}
-void GlobalFunctions::method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalFunctions::method_unescape(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
- RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined")));
+ ExecutionEngine *v4 = b->engine();
+ if (!argc)
+ RETURN_RESULT(v4->newString(QStringLiteral("undefined")));
- QString str = callData->args[0].toQString();
- RETURN_RESULT(scope.engine->newString(unescape(str)));
+ QString str = argv[0].toQString();
+ RETURN_RESULT(v4->newString(unescape(str)));
}
diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h
index 273f1ba7ea..fd1820c23c 100644
--- a/src/qml/jsruntime/qv4globalobject_p.h
+++ b/src/qml/jsruntime/qv4globalobject_p.h
@@ -69,23 +69,23 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject
{
V4_OBJECT2(EvalFunction, FunctionObject)
- void evalCall(Scope &scope, CallData *callData, bool directCall) const;
+ ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const;
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct GlobalFunctions
{
- static void method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_escape(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_parseInt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_parseFloat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isNaN(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isFinite(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_decodeURI(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_decodeURIComponent(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_encodeURI(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_encodeURIComponent(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_escape(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_unescape(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index 1edb7d3914..3e04ed63df 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -39,6 +39,7 @@
#include "qv4include_p.h"
#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include <QtQml/qjsengine.h>
#if QT_CONFIG(qml_network)
@@ -118,10 +119,10 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status)
if (!f)
return;
- QV4::ScopedCallData callData(scope, 1);
- callData->thisObject = v4->globalObject->asReturnedValue();
- callData->args[0] = status;
- f->call(scope, callData);
+ QV4::JSCallData jsCallData(scope, 1);
+ *jsCallData->thisObject = v4->globalObject->asReturnedValue();
+ jsCallData->args[0] = status;
+ f->call(jsCallData);
if (scope.hasException())
scope.engine->catchException();
}
@@ -195,9 +196,10 @@ void QV4Include::finished()
/*
Documented in qv8engine.cpp
*/
-void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QV4Include::method_include(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- if (!callData->argc)
+ QV4::Scope scope(b);
+ if (!callData->argc())
RETURN_UNDEFINED();
QQmlContextData *context = scope.engine->callingQmlContext();
@@ -206,7 +208,7 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope,
RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files")));
QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue());
- if (callData->argc >= 2 && callData->args[1].as<QV4::FunctionObject>())
+ if (callData->argc() >= 2 && callData->args[1].as<QV4::FunctionObject>())
callbackFunction = callData->args[1];
#if QT_CONFIG(qml_network)
@@ -260,13 +262,13 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope,
callback(callbackFunction, result);
}
- scope.result = result;
#else
QV4::ScopedValue result(scope);
result = resultValue(scope.engine, NetworkError);
callback(callbackFunction, result);
- scope.result = result;
#endif
+
+ return result->asReturnedValue();
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h
index 5908d6bfde..68537ba2e8 100644
--- a/src/qml/jsruntime/qv4include_p.h
+++ b/src/qml/jsruntime/qv4include_p.h
@@ -77,7 +77,7 @@ public:
Exception = 3
};
- static void method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_include(const QV4::BuiltinFunction *, QV4::CallData *callData);
private Q_SLOTS:
void finished();
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
new file mode 100644
index 0000000000..6d641bf9c5
--- /dev/null
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4JSCALL_H
+#define QV4JSCALL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4function_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4context_p.h"
+#include "qv4scopedvalue_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct JSCallData {
+ JSCallData(const Scope &scope, int argc = 0, const Value *argv = 0, const Value *thisObject = 0)
+ : scope(scope), argc(argc)
+ {
+ if (thisObject)
+ this->thisObject = const_cast<Value *>(thisObject);
+ else
+ this->thisObject = scope.alloc(1);
+ if (argv)
+ this->args = const_cast<Value *>(argv);
+ else
+ this->args = scope.alloc(argc);
+ }
+
+ JSCallData *operator->() {
+ return this;
+ }
+
+ CallData *callData(const FunctionObject *f = nullptr) const {
+ int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc;
+ CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop);
+ scope.engine->jsStackTop += size;
+ ptr->function = Encode::undefined();
+ ptr->context = Encode::undefined();
+ ptr->accumulator = Encode::undefined();
+ ptr->thisObject = thisObject->asReturnedValue();
+ ptr->setArgc(argc);
+ if (argc)
+ memcpy(ptr->args, args, argc*sizeof(Value));
+ if (f)
+ ptr->function = f->asReturnedValue();
+ return ptr;
+ }
+ const Scope &scope;
+ int argc;
+ Value *args;
+ Value *thisObject;
+};
+
+inline
+ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const
+{
+ return d()->jsConstruct(this, data.args, data.argc);
+}
+
+inline
+ReturnedValue FunctionObject::call(const JSCallData &data) const
+{
+ return d()->jsCall(this, data.thisObject, data.args, data.argc);
+}
+
+
+struct ScopedStackFrame {
+ Scope &scope;
+ CppStackFrame frame;
+
+ ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context)
+ : scope(scope)
+ {
+ frame.parent = 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 : 0;
+ scope.engine->currentStackFrame = &frame;
+ }
+ ~ScopedStackFrame() {
+ scope.engine->currentStackFrame = frame.parent;
+ }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4JSCALL_H
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index 0f021c8bd0..5e580b8b4d 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -46,6 +46,7 @@
#include <qv4runtime_p.h>
#include <qv4variantobject_p.h>
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
#include <qstack.h>
#include <qstringlist.h>
@@ -689,57 +690,57 @@ static QString quote(const QString &str)
QString Stringify::Str(const QString &key, const Value &v)
{
Scope scope(v4);
- scope.result = v;
- ScopedObject o(scope, scope.result);
+ ScopedValue value(scope, v);
+ ScopedObject o(scope, value);
if (o) {
ScopedString s(scope, v4->newString(QStringLiteral("toJSON")));
ScopedFunctionObject toJSON(scope, o->get(s));
if (!!toJSON) {
- ScopedCallData callData(scope, 1);
- callData->thisObject = scope.result;
- callData->args[0] = v4->newString(key);
- toJSON->call(scope, callData);
+ JSCallData jsCallData(scope, 1);
+ *jsCallData->thisObject = value;
+ jsCallData->args[0] = v4->newString(key);
+ value = toJSON->call(jsCallData);
}
}
if (replacerFunction) {
ScopedObject holder(scope, v4->newObject());
- holder->put(scope.engine->id_empty(), scope.result);
- ScopedCallData callData(scope, 2);
- callData->args[0] = v4->newString(key);
- callData->args[1] = scope.result;
- callData->thisObject = holder;
- replacerFunction->call(scope, callData);
+ holder->put(scope.engine->id_empty(), value);
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = v4->newString(key);
+ jsCallData->args[1] = value;
+ *jsCallData->thisObject = holder;
+ value = replacerFunction->call(jsCallData);
}
- o = scope.result.asReturnedValue();
+ o = value->asReturnedValue();
if (o) {
if (NumberObject *n = o->as<NumberObject>())
- scope.result = Encode(n->value());
+ value = Encode(n->value());
else if (StringObject *so = o->as<StringObject>())
- scope.result = so->d()->string;
+ value = so->d()->string;
else if (BooleanObject *b = o->as<BooleanObject>())
- scope.result = Encode(b->value());
+ value = Encode(b->value());
}
- if (scope.result.isNull())
+ if (value->isNull())
return QStringLiteral("null");
- if (scope.result.isBoolean())
- return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false");
- if (String *s = scope.result.stringValue())
- return quote(s->toQString());
-
- if (scope.result.isNumber()) {
- double d = scope.result.toNumber();
- return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null");
+ if (value->isBoolean())
+ return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false");
+ if (value->isString())
+ return quote(value->stringValue()->toQString());
+
+ if (value->isNumber()) {
+ double d = value->toNumber();
+ return std::isfinite(d) ? value->toQString() : QStringLiteral("null");
}
- if (const QV4::VariantObject *v = scope.result.as<QV4::VariantObject>()) {
+ if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) {
return v->d()->data().toString();
}
- o = scope.result.asReturnedValue();
+ o = value->asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
if (o->as<ArrayObject>()) {
@@ -883,25 +884,28 @@ void Heap::JsonObject::init()
}
-void JsonObject::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue JsonObject::method_parse(const BuiltinFunction *b, CallData *callData)
{
- ScopedValue v(scope, callData->argument(0));
- QString jtext = v->toQString();
+ ExecutionEngine *v4 = b->engine();
+ QString jtext;
+ if (callData->argc() > 0)
+ jtext = callData->args[0].toQString();
DEBUG << "parsing source = " << jtext;
- JsonParser parser(scope.engine, jtext.constData(), jtext.length());
+ JsonParser parser(v4, jtext.constData(), jtext.length());
QJsonParseError error;
- ScopedValue result(scope, parser.parse(&error));
+ ReturnedValue result = parser.parse(&error);
if (error.error != QJsonParseError::NoError) {
DEBUG << "parse error" << error.errorString();
- RETURN_RESULT(scope.engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")));
+ RETURN_RESULT(v4->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")));
}
- scope.result = result;
+ return result;
}
-void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue JsonObject::method_stringify(const BuiltinFunction *b, CallData *callData)
{
+ Scope scope(b);
Stringify stringify(scope.engine);
ScopedObject o(scope, callData->argument(1));
@@ -946,7 +950,7 @@ void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallDat
QString result = stringify.Str(QString(), arg0);
if (result.isEmpty() || scope.engine->hasException)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h
index a73ce1c74e..19dba14aef 100644
--- a/src/qml/jsruntime/qv4jsonobject_p.h
+++ b/src/qml/jsruntime/qv4jsonobject_p.h
@@ -88,8 +88,8 @@ private:
typedef QSet<ObjectItem> V4ObjectSet;
public:
- static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_parse(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_stringify(const BuiltinFunction *, CallData *callData);
static ReturnedValue fromJsonValue(ExecutionEngine *engine, const QJsonValue &value);
static ReturnedValue fromJsonObject(ExecutionEngine *engine, const QJsonObject &object);
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index afadf59bd5..c603d424ed 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qv4lookup_p.h"
#include "qv4functionobject_p.h"
-#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include "qv4string_p.h"
#include <private/qv4identifiertable_p.h>
@@ -50,7 +50,7 @@ using namespace QV4;
ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttributes *attrs)
{
ExecutionEngine *engine = o->engine();
- Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
int i = 0;
Heap::Object *obj = o->d();
while (i < Size && obj) {
@@ -86,7 +86,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs
{
Heap::Object *obj = thisObject->d();
ExecutionEngine *engine = thisObject->engine();
- Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
int i = 0;
while (i < Size && obj) {
classList[i] = obj->internalClass;
@@ -117,140 +117,6 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs
return Primitive::emptyValue().asReturnedValue();
}
-ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index)
-{
- uint idx;
- if (object.isObject() && index.asArrayIndex(idx)) {
- l->indexedGetter = indexedGetterObjectInt;
- return indexedGetterObjectInt(l, engine, object, index);
- }
- return indexedGetterFallback(l, engine, object, index);
-}
-
-ReturnedValue Lookup::indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index)
-{
- Q_UNUSED(l);
- Scope scope(engine);
- uint idx = 0;
- bool isInt = index.asArrayIndex(idx);
-
- ScopedObject o(scope, object);
- if (!o) {
- if (isInt) {
- if (const String *str = object.as<String>()) {
- if (idx >= (uint)str->toQString().length()) {
- return Encode::undefined();
- }
- const QString s = str->toQString().mid(idx, 1);
- return scope.engine->newString(s)->asReturnedValue();
- }
- }
-
- if (object.isNullOrUndefined()) {
- QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow());
- return engine->throwTypeError(message);
- }
-
- o = RuntimeHelpers::convertToObject(scope.engine, object);
- if (!o) // type error
- return Encode::undefined();
- }
-
- if (isInt) {
- if (o->d()->arrayData && !o->d()->arrayData->attrs) {
- ScopedValue v(scope, Scoped<ArrayData>(scope, o->arrayData())->get(idx));
- if (!v->isEmpty())
- return v->asReturnedValue();
- }
-
- return o->getIndexed(idx);
- }
-
- ScopedString name(scope, index.toString(scope.engine));
- if (scope.hasException())
- return Encode::undefined();
- return o->get(name);
-
-}
-
-
-ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index)
-{
- uint idx;
- if (index.asArrayIndex(idx)) {
- if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
- Heap::Object *o = static_cast<Heap::Object *>(b);
- if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
- if (idx < s->values.size)
- if (!s->data(idx).isEmpty())
- return s->data(idx).asReturnedValue();
- }
- }
- }
- }
-
- return indexedGetterFallback(l, engine, object, index);
-}
-
-void Lookup::indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v)
-{
- if (Object *o = object.objectValue()) {
- uint idx;
- if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple && index.asArrayIndex(idx)) {
- l->indexedSetter = indexedSetterObjectInt;
- indexedSetterObjectInt(l, engine, object, index, v);
- return;
- }
- }
- indexedSetterFallback(l, engine, object, index, v);
-}
-
-void Lookup::indexedSetterFallback(Lookup *, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
-{
- Scope scope(engine);
- ScopedObject o(scope, object.toObject(scope.engine));
- if (scope.engine->hasException)
- return;
-
- uint idx;
- if (index.asArrayIndex(idx)) {
- if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
- if (idx < s->values.size) {
- s->setData(engine, idx, value);
- return;
- }
- }
- o->putIndexed(idx, value);
- return;
- }
-
- ScopedString name(scope, index.toString(scope.engine));
- o->put(name, value);
-}
-
-void Lookup::indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v)
-{
- uint idx;
- if (index.asArrayIndex(idx)) {
- if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
- Heap::Object *o = static_cast<Heap::Object *>(b);
- if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
- if (idx < s->values.size) {
- s->setData(engine, idx, v);
- return;
- }
- }
- }
- }
- }
- indexedSetterFallback(l, engine, object, index, v);
-}
-
ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (const Object *o = object.as<Object>())
@@ -268,7 +134,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va
Q_ASSERT(object.isString());
proto = engine->stringPrototype();
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
if (name->equals(engine->id_length())) {
// special case, as the property is on the object itself
l->getter = stringLengthGetter;
@@ -289,9 +155,10 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va
if (attrs.isData()) {
if (l->level == 0) {
uint nInline = l->proto->vtable()->nInlineProperties;
- if (l->index < nInline)
+ if (l->index < nInline) {
+ l->index += l->proto->vtable()->inlinePropertyOffset;
l->getter = Lookup::primitiveGetter0Inline;
- else {
+ } else {
l->index -= nInline;
l->getter = Lookup::primitiveGetter0MemberData;
}
@@ -369,7 +236,7 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V
QV4::ScopedObject o(scope, object.toObject(scope.engine));
if (!o)
return Encode::undefined();
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
return o->get(name);
}
@@ -392,7 +259,7 @@ ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Va
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->inlinePropertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
}
@@ -435,9 +302,9 @@ ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *eng
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->inlinePropertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
if (l->classList[2] == o->internalClass)
- return o->inlinePropertyData(l->index2)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index2)->asReturnedValue();
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -450,7 +317,7 @@ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->inlinePropertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
if (l->classList[2] == o->internalClass)
return o->memberData->values.data()[l->index2].asReturnedValue();
}
@@ -480,7 +347,7 @@ ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, c
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->inlinePropertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass)
return o->prototype()->propertyData(l->index2)->asReturnedValue();
}
@@ -534,10 +401,9 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = object;
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = object;
+ return getter->call(jsCallData);
}
}
l->getter = getterFallback;
@@ -557,10 +423,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = object;
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = object;
+ return getter->call(jsCallData);
}
}
l->getter = getterFallback;
@@ -583,10 +448,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = object;
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = object;
+ return getter->call(jsCallData);
}
}
}
@@ -600,7 +464,7 @@ ReturnedValue Lookup::primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine,
if (object.type() == l->type) {
Heap::Object *o = l->proto;
if (l->classList[0] == o->internalClass)
- return o->inlinePropertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
}
l->getter = getterGeneric;
return getterGeneric(l, engine, object);
@@ -639,10 +503,9 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = object;
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = object;
+ return getter->call(jsCallData);
}
}
l->getter = getterGeneric;
@@ -660,10 +523,9 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = object;
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = object;
+ return getter->call(jsCallData);
}
}
l->getter = getterGeneric;
@@ -698,9 +560,10 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
if (attrs.isData()) {
if (l->level == 0) {
uint nInline = o->d()->vtable()->nInlineProperties;
- if (l->index < nInline)
+ if (l->index < nInline) {
+ l->index += o->d()->vtable()->inlinePropertyOffset;
l->globalGetter = globalGetter0Inline;
- else {
+ } else {
l->index -= nInline;
l->globalGetter = globalGetter0MemberData;
}
@@ -720,7 +583,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
}
}
Scope scope(engine);
- ScopedString n(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedString n(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
return engine->throwReferenceError(n);
}
@@ -728,7 +591,7 @@ ReturnedValue Lookup::globalGetter0Inline(Lookup *l, ExecutionEngine *engine)
{
Object *o = engine->globalObject;
if (l->classList[0] == o->internalClass())
- return o->d()->inlinePropertyData(l->index)->asReturnedValue();
+ return o->d()->inlinePropertyDataWithOffset(l->index)->asReturnedValue();
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -780,10 +643,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine)
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = Primitive::undefinedValue();
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ return getter->call(jsCallData);
}
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -799,10 +660,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine)
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = Primitive::undefinedValue();
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ return getter->call(jsCallData);
}
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -821,10 +680,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine)
if (!getter)
return Encode::undefined();
- ScopedCallData callData(scope, 0);
- callData->thisObject = Primitive::undefinedValue();
- getter->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ return getter->call(jsCallData);
}
}
}
@@ -832,87 +689,88 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine)
return globalGetterGeneric(l, engine);
}
-void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Scope scope(engine);
ScopedObject o(scope, object);
if (!o) {
o = RuntimeHelpers::convertToObject(scope.engine, object);
if (!o) // type error
- return;
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
- o->put(name, value);
- return;
+ return false;
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return o->put(name, value);
}
- o->setLookup(l, value);
+ return o->setLookup(l, value);
}
-void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Lookup l1 = *l;
if (Object *o = object.as<Object>()) {
- o->setLookup(l, value);
+ if (!o->setLookup(l, value))
+ return false;
if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) {
l->setter = setter0setter0;
l->classList[1] = l1.classList[0];
l->index2 = l1.index;
- return;
+ return true;
}
}
l->setter = setterFallback;
- setterFallback(l, engine, object, value);
+ return setterFallback(l, engine, object, value);
}
-void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
QV4::Scope scope(engine);
QV4::ScopedObject o(scope, object.toObject(scope.engine));
- if (o) {
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
- o->put(name, value);
- }
+ if (!o)
+ return false;
+
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return o->put(name, value);
}
-void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
o->setProperty(engine, l->index, value);
- return;
+ return true;
}
- setterTwoClasses(l, engine, object, value);
+ return setterTwoClasses(l, engine, object, value);
}
-void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
o->d()->setInlineProperty(engine, l->index, value);
- return;
+ return true;
}
- setterTwoClasses(l, engine, object, value);
+ return setterTwoClasses(l, engine, object, value);
}
-void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
Q_ASSERT(!o->prototype());
o->setInternalClass(l->classList[3]);
o->setProperty(l->index, value);
- return;
+ return true;
}
l->setter = setterFallback;
- setterFallback(l, engine, object, value);
+ return setterFallback(l, engine, object, value);
}
-void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
@@ -922,15 +780,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co
Q_ASSERT(!p->prototype());
o->setInternalClass(l->classList[3]);
o->setProperty(l->index, value);
- return;
+ return true;
}
}
l->setter = setterFallback;
- setterFallback(l, engine, object, value);
+ return setterFallback(l, engine, object, value);
}
-void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
@@ -943,32 +801,31 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co
Q_ASSERT(!p->prototype());
o->setInternalClass(l->classList[3]);
o->setProperty(l->index, value);
- return;
+ return true;
}
}
}
l->setter = setterFallback;
- setterFallback(l, engine, object, value);
+ return setterFallback(l, engine, object, value);
}
-void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
if (o) {
if (o->internalClass() == l->classList[0]) {
o->setProperty(l->index, value);
- return;
+ return true;
}
if (o->internalClass() == l->classList[1]) {
o->setProperty(l->index2, value);
- return;
+ return true;
}
}
l->setter = setterFallback;
- setterFallback(l, engine, object, value);
-
+ return setterFallback(l, engine, object, value);
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index ce5189a780..826760aa2d 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -67,11 +67,9 @@ namespace QV4 {
struct Lookup {
enum { Size = 4 };
union {
- ReturnedValue (*indexedGetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index);
- void (*indexedSetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v);
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
- void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
+ bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
union {
InternalClass *classList[Size];
@@ -90,14 +88,6 @@ struct Lookup {
uint index;
uint nameIndex;
- static ReturnedValue indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index);
- static ReturnedValue indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index);
- static ReturnedValue indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index);
-
- static void indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v);
- static void indexedSetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value);
- static void indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v);
-
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -133,15 +123,15 @@ struct Lookup {
static ReturnedValue globalGetterAccessor1(Lookup *l, ExecutionEngine *engine);
static ReturnedValue globalGetterAccessor2(Lookup *l, ExecutionEngine *engine);
- static void setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static void setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
ReturnedValue lookup(const Value &thisObject, Object *obj, PropertyAttributes *attrs);
ReturnedValue lookup(const Object *obj, PropertyAttributes *attrs);
@@ -151,7 +141,6 @@ struct Lookup {
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
// Ensure that these offsets are always at this point to keep generated code compatible
// across 32-bit and 64-bit (matters when cross-compiling).
-Q_STATIC_ASSERT(offsetof(Lookup, indexedGetter) == 0);
Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0);
}
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index e00eaa0d9b..200380eda0 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -49,7 +49,6 @@ const VTable Managed::static_vtbl =
0,
0,
0,
- 0,
Managed::IsExecutionContext,
Managed::IsString,
Managed::IsObject,
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 40dfc244ea..f81dcf9479 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -93,7 +93,6 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
dptr->_checkIsInitialized(); \
return dptr; \
} \
- static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; \
V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass)
#define V4_MANAGED(DataClass, superClass) \
@@ -132,9 +131,8 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
{ \
parentVTable, \
- markTable, \
(sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
- (sizeof(classname::Data) + (std::is_same<classname, Object>::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
+ (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
- (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
classname::IsExecutionContext, \
classname::IsString, \
@@ -146,9 +144,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
classname::MyType, \
#classname, \
Q_VTABLE_FUNCTION(classname, destroy), \
- Q_VTABLE_FUNCTION(classname, markObjects), \
+ classname::Data::markObjects, \
isEqualTo \
-}
+} \
#define DEFINE_MANAGED_VTABLE(classname) \
QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
@@ -176,6 +174,7 @@ private:
Q_DISABLE_COPY(Managed)
public:
+ enum { NInlineProperties = 0 };
enum Type {
Type_Invalid,
@@ -221,7 +220,6 @@ public:
inline void mark(MarkStack *markStack);
static void destroy(Heap::Base *) {}
- static void markObjects(Heap::Base *, MarkStack *) {}
Q_ALWAYS_INLINE Heap::Base *heapObject() const {
return m();
@@ -240,6 +238,11 @@ private:
friend struct ObjectIterator;
};
+inline void Managed::mark(MarkStack *markStack)
+{
+ Q_ASSERT(m());
+ m()->mark(markStack);
+}
template<>
inline const Managed *Value::as() const {
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index 46230b5bc1..252ec345d9 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -94,54 +94,54 @@ static Q_ALWAYS_INLINE double copySign(double x, double y)
return ::copysign(x, y);
}
-void MathObject::method_abs(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_abs(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc)
+ if (!argc)
RETURN_RESULT(Encode(qt_qnan()));
- if (callData->args[0].isInteger()) {
- int i = callData->args[0].integerValue();
+ if (argv[0].isInteger()) {
+ int i = argv[0].integerValue();
RETURN_RESULT(Encode(i < 0 ? - i : i));
}
- double v = callData->args[0].toNumber();
+ double v = argv[0].toNumber();
if (v == 0) // 0 | -0
RETURN_RESULT(Encode(0));
RETURN_RESULT(Encode(v < 0 ? -v : v));
}
-void MathObject::method_acos(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : 2;
+ double v = argc ? argv[0].toNumber() : 2;
if (v > 1)
RETURN_RESULT(Encode(qt_qnan()));
RETURN_RESULT(Encode(std::acos(v)));
}
-void MathObject::method_asin(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : 2;
+ double v = argc ? argv[0].toNumber() : 2;
if (v > 1)
RETURN_RESULT(Encode(qt_qnan()));
else
RETURN_RESULT(Encode(std::asin(v)));
}
-void MathObject::method_atan(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (v == 0.0)
RETURN_RESULT(Encode(v));
else
RETURN_RESULT(Encode(std::atan(v)));
}
-void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v1 = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- double v2 = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan();
+ double v1 = argc ? argv[0].toNumber() : qt_qnan();
+ double v2 = argc > 1 ? argv[1].toNumber() : qt_qnan();
if ((v1 < 0) && qt_is_finite(v1) && qt_is_inf(v2) && (copySign(1.0, v2) == 1.0))
RETURN_RESULT(Encode(copySign(0, -1.0)));
@@ -156,24 +156,24 @@ void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *c
RETURN_RESULT(Encode(std::atan2(v1, v2)));
}
-void MathObject::method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (v < 0.0 && v > -1.0)
RETURN_RESULT(Encode(copySign(0, -1.0)));
else
RETURN_RESULT(Encode(std::ceil(v)));
}
-void MathObject::method_cos(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::cos(v)));
}
-void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (qt_is_inf(v)) {
if (copySign(1.0, v) == -1.0)
RETURN_RESULT(Encode(0));
@@ -184,37 +184,39 @@ void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *cal
}
}
-void MathObject::method_floor(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
- RETURN_RESULT(Encode(std::floor(v)));
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ Value result = Primitive::fromDouble(std::floor(v));
+ result.isInt32();
+ RETURN_RESULT(result);
}
-void MathObject::method_log(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (v < 0)
RETURN_RESULT(Encode(qt_qnan()));
else
RETURN_RESULT(Encode(std::log(v)));
}
-void MathObject::method_max(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double mx = -qt_inf();
- for (int i = 0; i < callData->argc; ++i) {
- double x = callData->args[i].toNumber();
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ double x = argv[i].toNumber();
if (x > mx || std::isnan(x))
mx = x;
}
RETURN_RESULT(Encode(mx));
}
-void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double mx = qt_inf();
- for (int i = 0; i < callData->argc; ++i) {
- double x = callData->args[i].toNumber();
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ double x = argv[i].toNumber();
if ((x == 0 && mx == x && copySign(1.0, x) == -1.0)
|| (x < mx) || std::isnan(x)) {
mx = x;
@@ -223,10 +225,10 @@ void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *cal
RETURN_RESULT(Encode(mx));
}
-void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double x = callData->argc > 0 ? callData->args[0].toNumber() : qt_qnan();
- double y = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan();
+ double x = argc > 0 ? argv[0].toNumber() : qt_qnan();
+ double y = argc > 1 ? argv[1].toNumber() : qt_qnan();
if (std::isnan(y))
RETURN_RESULT(Encode(qt_qnan()));
@@ -273,21 +275,21 @@ void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *cal
RETURN_RESULT(Encode(qt_qnan()));
}
-void MathObject::method_random(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, const Value *, int)
{
RETURN_RESULT(Encode(QRandomGenerator::getReal()));
}
-void MathObject::method_round(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
v = copySign(std::floor(v + 0.5), v);
RETURN_RESULT(Encode(v));
}
-void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (std::isnan(v))
RETURN_RESULT(Encode(qt_qnan()));
@@ -298,21 +300,21 @@ void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *ca
RETURN_RESULT(Encode(std::signbit(v) ? -1 : 1));
}
-void MathObject::method_sin(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::sin(v)));
}
-void MathObject::method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::sqrt(v)));
}
-void MathObject::method_tan(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double v = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double v = argc ? argv[0].toNumber() : qt_qnan();
if (v == 0.0)
RETURN_RESULT(Encode(v));
else
diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h
index e617712905..0bf5da9404 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -69,25 +69,25 @@ struct MathObject: Object
V4_OBJECT2(MathObject, Object)
Q_MANAGED_TYPE(MathObject)
- static void method_abs(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_acos(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_asin(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_atan(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_cos(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_exp(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_floor(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_log(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_max(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_min(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_pow(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_random(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_round(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_sign(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_sin(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_tan(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_random(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index bae524d088..3e231d693b 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -63,7 +63,7 @@ namespace Heap {
Member(class, ValueArray, ValueArray, values)
DECLARE_HEAP_OBJECT(MemberData, Base) {
- DECLARE_MARK_TABLE(MemberData);
+ DECLARE_MARKOBJECTS(MemberData);
};
V4_ASSERT_IS_TRIVIAL(MemberData)
diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp
index d5c80dbf80..f58ff45801 100644
--- a/src/qml/jsruntime/qv4numberobject.cpp
+++ b/src/qml/jsruntime/qv4numberobject.cpp
@@ -78,16 +78,16 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Number"));
}
-void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
- scope.result = Encode(scope.engine->newNumberObject(dbl));
+ double dbl = argc ? argv[0].toNumber() : 0.;
+ return Encode(f->engine()->newNumberObject(dbl));
}
-void NumberCtor::call(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
- scope.result = Encode(dbl);
+ double dbl = argc ? argv[0].toNumber() : 0.;
+ return Encode(dbl);
}
void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -124,119 +124,101 @@ QT_WARNING_POP
defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1);
}
-inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData)
+inline ReturnedValue thisNumberValue(ExecutionEngine *v4, const Value *thisObject)
{
- if (callData->thisObject.isNumber())
- return callData->thisObject.asReturnedValue();
- NumberObject *n = callData->thisObject.as<NumberObject>();
+ if (thisObject->isNumber())
+ return thisObject->asReturnedValue();
+ const NumberObject *n = thisObject->as<NumberObject>();
if (!n) {
- scope.engine->throwTypeError();
+ v4->throwTypeError();
return Encode::undefined();
}
return Encode(n->value());
}
-inline double thisNumber(Scope &scope, CallData *callData)
+inline double thisNumber(ExecutionEngine *engine, const Value *thisObject)
{
- if (callData->thisObject.isNumber())
- return callData->thisObject.asDouble();
- NumberObject *n = callData->thisObject.as<NumberObject>();
+ if (thisObject->isNumber())
+ return thisObject->asDouble();
+ const NumberObject *n = thisObject->as<NumberObject>();
if (!n) {
- scope.engine->throwTypeError();
+ engine->throwTypeError();
return 0;
}
return n->value();
}
-void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc) {
- scope.result = Encode(false);
- return;
- }
+ if (!argc)
+ return Encode(false);
- double v = callData->args[0].toNumber();
- scope.result = Encode(!std::isnan(v) && !qt_is_inf(v));
+ double v = argv[0].toNumber();
+ return Encode(!std::isnan(v) && !qt_is_inf(v));
}
-void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_isInteger(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc) {
- scope.result = Encode(false);
- return;
- }
+ if (!argc)
+ return Encode(false);
- const Value &v = callData->args[0];
- if (!v.isNumber()) {
- scope.result = Encode(false);
- return;
- }
+ const Value &v = argv[0];
+ if (!v.isNumber())
+ return Encode(false);
double dv = v.toNumber();
- if (std::isnan(dv) || qt_is_inf(dv)) {
- scope.result = Encode(false);
- return;
- }
+ if (std::isnan(dv) || qt_is_inf(dv))
+ return Encode(false);
double iv = v.toInteger();
- scope.result = Encode(dv == iv);
+ return Encode(dv == iv);
}
-void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_isSafeInteger(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc) {
- scope.result = Encode(false);
- return;
- }
+ if (!argc)
+ return Encode(false);
- const Value &v = callData->args[0];
- if (!v.isNumber()) {
- scope.result = Encode(false);
- return;
- }
+ const Value &v = argv[0];
+ if (!v.isNumber())
+ return Encode(false);
double dv = v.toNumber();
- if (std::isnan(dv) || qt_is_inf(dv)) {
- scope.result = Encode(false);
- return;
- }
+ if (std::isnan(dv) || qt_is_inf(dv))
+ return Encode(false);
double iv = v.toInteger();
- scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1);
+ return Encode(dv == iv && std::fabs(iv) <= (2^53)-1);
}
-void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!callData->argc) {
- scope.result = Encode(false);
- return;
- }
+ if (!argc)
+ return Encode(false);
- double v = callData->args[0].toNumber();
+ double v = argv[0].toNumber();
// cast to bool explicitly as std::isnan() may give us ::isnan(), which
// sometimes returns an int and we don't want the Encode(int) overload.
- scope.result = Encode(bool(std::isnan(v)));
+ return Encode(bool(std::isnan(v)));
}
-void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- double num = thisNumber(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ double num = thisNumber(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- if (callData->argc && !callData->args[0].isUndefined()) {
- int radix = callData->args[0].toInt32();
+ if (argc && !argv[0].isUndefined()) {
+ int radix = argv[0].toInt32();
if (radix < 2 || radix > 36) {
- scope.result = scope.engine->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix")
- .arg(radix));
- return;
+ return v4->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix").arg(radix));
}
if (std::isnan(num)) {
- scope.result = scope.engine->newString(QStringLiteral("NaN"));
- return;
+ return Encode(v4->newString(QStringLiteral("NaN")));
} else if (qt_is_inf(num)) {
- scope.result = scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity"));
- return;
+ return Encode(v4->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity")));
}
if (radix != 10) {
@@ -266,43 +248,42 @@ void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, Cal
}
if (negative)
str.prepend(QLatin1Char('-'));
- scope.result = scope.engine->newString(str);
- return;
+ return Encode(v4->newString(str));
}
}
- scope.result = Primitive::fromDouble(num).toString(scope.engine);
+ return Encode(Primitive::fromDouble(num).toString(v4));
}
-void NumberPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedValue v(scope, thisNumberValue(scope, callData));
- scope.result = v->toString(scope.engine);
- CHECK_EXCEPTION();
+ Scope scope(b);
+ ScopedValue v(scope, thisNumberValue(b->engine(), thisObject));
+ return Encode(v->toString(scope.engine));
}
-void NumberPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- scope.result = thisNumberValue(scope, callData);
+ return thisNumberValue(b->engine(), thisObject);
}
-void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_toFixed(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- double v = thisNumber(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ double v = thisNumber(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
double fdigits = 0;
- if (callData->argc > 0)
- fdigits = callData->args[0].toInteger();
+ if (argc > 0)
+ fdigits = argv[0].toInteger();
if (std::isnan(fdigits))
fdigits = 0;
- if (fdigits < 0 || fdigits > 20) {
- scope.result = scope.engine->throwRangeError(callData->thisObject);
- return;
- }
+ if (fdigits < 0 || fdigits > 20)
+ return v4->throwRangeError(*thisObject);
QString str;
if (std::isnan(v))
@@ -312,49 +293,51 @@ void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, Call
else if (v < 1.e21)
str = NumberLocale::instance()->toString(v, 'f', int(fdigits));
else {
- scope.result = RuntimeHelpers::stringFromNumber(scope.engine, v);
- return;
+ return Encode(RuntimeHelpers::stringFromNumber(v4, v));
}
- scope.result = scope.engine->newString(str);
+ return Encode(v4->newString(str));
}
-void NumberPrototype::method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_toExponential(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- double d = thisNumber(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ double d = thisNumber(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
int fdigits = NumberLocale::instance()->defaultDoublePrecision;
- if (callData->argc && !callData->args[0].isUndefined()) {
- fdigits = callData->args[0].toInt32();
+ if (argc && !argv[0].isUndefined()) {
+ fdigits = argv[0].toInt32();
if (fdigits < 0 || fdigits > 20) {
- ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
- scope.result = scope.engine->throwRangeError(error);
- return;
+ Scope scope(v4);
+ ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
+ return v4->throwRangeError(error);
}
}
QString result = NumberLocale::instance()->toString(d, 'e', fdigits);
- scope.result = scope.engine->newString(result);
+ return Encode(v4->newString(result));
}
-void NumberPrototype::method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedValue v(scope, thisNumberValue(scope, callData));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ ScopedValue v(scope, thisNumberValue(scope.engine, thisObject));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- if (!callData->argc || callData->args[0].isUndefined()) {
- scope.result = v->toString(scope.engine);
- return;
- }
- int precision = callData->args[0].toInt32();
+ if (!argc || argv[0].isUndefined())
+ return Encode(v->toString(scope.engine));
+
+ int precision = argv[0].toInt32();
if (precision < 1 || precision > 21) {
ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")));
- scope.result = scope.engine->throwRangeError(error);
- return;
+ return scope.engine->throwRangeError(error);
}
QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision);
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h
index 0bc2cd8c65..cfdcf9bc1d 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject
{
V4_OBJECT2(NumberCtor, FunctionObject)
- static void construct(const Managed *that, Scope &scope, CallData *callData);
- static void call(const Managed *, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct NumberPrototype: NumberObject
@@ -88,16 +88,16 @@ struct NumberPrototype: NumberObject
V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
- static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_isFinite(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isInteger(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isSafeInteger(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isNaN(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toFixed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toExponential(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toPrecision(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 94d5a74fe5..97296eb847 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -38,8 +38,6 @@
****************************************************************************/
#include "qv4object_p.h"
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
#include "qv4argumentsobject_p.h"
@@ -51,6 +49,7 @@
#include "qv4identifier_p.h"
#include "qv4string_p.h"
#include "qv4identifiertable_p.h"
+#include "qv4jscall_p.h"
#include <stdint.h>
@@ -108,10 +107,9 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
return Encode::undefined();
Scope scope(f->engine());
- ScopedCallData callData(scope);
- callData->thisObject = thisObject;
- f->call(scope, callData);
- return scope.result.asReturnedValue();
+ JSCallData jsCallData(scope);
+ *jsCallData->thisObject = thisObject;
+ return f->call(jsCallData);
}
bool Object::putValue(uint memberIndex, const Value &value)
@@ -127,25 +125,20 @@ bool Object::putValue(uint memberIndex, const Value &value)
if (set) {
Scope scope(ic->engine);
ScopedFunctionObject setter(scope, set);
- ScopedCallData callData(scope, 1);
- callData->args[0] = value;
- callData->thisObject = this;
- setter->call(scope, callData);
+ JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = value;
+ *jsCallData->thisObject = this;
+ setter->call(jsCallData);
return !ic->engine->hasException;
}
- goto reject;
+ return false;
}
if (!attrs.isWritable())
- goto reject;
+ return false;
setProperty(memberIndex, value);
return true;
-
- reject:
- if (engine()->current->strictMode)
- engine()->throwTypeError();
- return false;
}
void Object::defineDefaultProperty(const QString &name, const Value &value)
@@ -156,7 +149,28 @@ void Object::defineDefaultProperty(const QString &name, const Value &value)
defineDefaultProperty(s, value);
}
-void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
+void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount)
+{
+ ExecutionEngine *e = engine();
+ Scope scope(e);
+ ScopedString s(scope, e->newIdentifier(name));
+ ExecutionContext *global = e->rootContext();
+ ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code));
+ function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
+ defineDefaultProperty(s, function);
+}
+
+void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount)
+{
+ ExecutionEngine *e = engine();
+ Scope scope(e);
+ ExecutionContext *global = e->rootContext();
+ ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code));
+ function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
+ defineDefaultProperty(name, function);
+}
+
+void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -167,7 +181,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built
defineDefaultProperty(s, function);
}
-void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
+void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -177,8 +191,29 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct
defineDefaultProperty(name, function);
}
-void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
- void (*setter)(const BuiltinFunction *, Scope &, CallData *))
+void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *),
+ ReturnedValue (*setter)(const BuiltinFunction *, CallData *))
+{
+ ExecutionEngine *e = engine();
+ Scope scope(e);
+ ScopedString s(scope, e->newIdentifier(name));
+ defineAccessorProperty(s, getter, setter);
+}
+
+void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *),
+ ReturnedValue (*setter)(const BuiltinFunction *, CallData *))
+{
+ ExecutionEngine *v4 = engine();
+ QV4::Scope scope(v4);
+ ScopedProperty p(scope);
+ ExecutionContext *global = v4->rootContext();
+ p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0)));
+ p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0)));
+ insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
+}
+
+void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
+ ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -186,8 +221,8 @@ void Object::defineAccessorProperty(const QString &name, void (*getter)(const Bu
defineAccessorProperty(s, getter, setter);
}
-void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
- void (*setter)(const BuiltinFunction *, Scope &, CallData *))
+void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
+ ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
{
ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
@@ -198,6 +233,8 @@ void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFu
insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
}
+
+
void Object::defineReadonlyProperty(const QString &name, const Value &value)
{
QV4::ExecutionEngine *e = engine();
@@ -224,9 +261,21 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value
insertMember(name, value, Attr_ReadOnly_ButConfigurable);
}
-void Object::markObjects(Heap::Base *b, MarkStack *stack)
+void Object::markObjects(Heap::Base *base, MarkStack *stack)
{
- Heap::Object *o = static_cast<Heap::Object *>(b);
+ Heap::Object::markObjects(base, stack);
+}
+
+void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
+{
+ Object *o = static_cast<Object *>(b);
+ if (o->memberData)
+ o->memberData->mark(stack);
+ if (o->arrayData) {
+ o->arrayData->setMarkBit();
+ if (o->arrayData->needsMark)
+ ArrayData::markObjects(o->arrayData, stack);
+ }
uint nInline = o->vtable()->nInlineProperties;
Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset;
const Value *end = v + nInline;
@@ -402,14 +451,14 @@ bool Object::hasOwnProperty(uint index) const
return false;
}
-void Object::construct(const Managed *m, Scope &scope, CallData *)
+ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int)
{
- scope.result = static_cast<const Object *>(m)->engine()->throwTypeError();
+ return f->engine()->throwTypeError();
}
-void Object::call(const Managed *m, Scope &scope, CallData *)
+ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int)
{
- scope.result = static_cast<const Object *>(m)->engine()->throwTypeError();
+ return f->engine()->throwTypeError();
}
ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty)
@@ -483,9 +532,10 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l)
Q_ASSERT(l->classList[0] == o->internalClass());
if (l->level == 0) {
uint nInline = o->d()->vtable()->nInlineProperties;
- if (l->index < nInline)
+ if (l->index < nInline) {
+ l->index += o->d()->vtable()->inlinePropertyOffset;
l->getter = Lookup::getter0Inline;
- else {
+ } else {
l->index -= nInline;
l->getter = Lookup::getter0MemberData;
}
@@ -512,11 +562,11 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l)
return Encode::undefined();
}
-void Object::setLookup(Managed *m, Lookup *l, const Value &value)
+bool Object::setLookup(Managed *m, Lookup *l, const Value &value)
{
Scope scope(static_cast<Object *>(m)->engine());
ScopedObject o(scope, static_cast<Object *>(m));
- ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
InternalClass *c = o->internalClass();
uint idx = c->find(name);
@@ -526,42 +576,44 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value)
l->index = idx;
l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
o->setProperty(idx, value);
- return;
+ return true;
}
- if (idx != UINT_MAX) {
- o->putValue(idx, value);
- return;
- }
+ if (idx != UINT_MAX)
+ return o->putValue(idx, value);
}
- o->put(name, value);
+ if (!o->put(name, value)) {
+ l->setter = Lookup::setterFallback;
+ return false;
+ }
if (o->internalClass() == c)
- return;
+ return true;
idx = o->internalClass()->find(name);
- if (idx == UINT_MAX)
- return;
+ if (idx == UINT_MAX) // ### can this even happen?
+ return false;
l->classList[0] = c;
l->classList[3] = o->internalClass();
l->index = idx;
if (!o->prototype()) {
l->setter = Lookup::setterInsert0;
- return;
+ return true;
}
o = o->prototype();
l->classList[1] = o->internalClass();
if (!o->prototype()) {
l->setter = Lookup::setterInsert1;
- return;
+ return true;
}
o = o->prototype();
l->classList[2] = o->internalClass();
if (!o->prototype()) {
l->setter = Lookup::setterInsert2;
- return;
+ return true;
}
l->setter = Lookup::setterGeneric;
+ return true;
}
void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs)
@@ -644,14 +696,13 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
name->makeIdentifier();
Identifier *id = name->identifier();
- Scope scope(engine());
- ScopedObject o(scope, this);
+ Heap::Object *o = d();
while (o) {
- uint idx = o->internalClass()->find(id);
+ uint idx = o->internalClass->find(id);
if (idx < UINT_MAX) {
if (hasProperty)
*hasProperty = true;
- return getValue(*o->propertyData(idx), o->internalClass()->propertyData.at(idx));
+ return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx));
}
o = o->prototype();
@@ -725,9 +776,9 @@ bool Object::internalPut(String *name, const Value &value)
if (attrs.isAccessor()) {
if (memberIndex->as<FunctionObject>())
goto cont;
- goto reject;
+ return false;
} else if (!attrs.isWritable())
- goto reject;
+ return false;
else if (isArrayObject() && name->equals(engine->id_length())) {
bool ok;
uint l = value.asArrayLength(&ok);
@@ -737,14 +788,14 @@ bool Object::internalPut(String *name, const Value &value)
}
ok = setArrayLength(l);
if (!ok)
- goto reject;
+ return false;
} else {
memberIndex.set(engine, value);
}
return true;
} else if (!prototype()) {
if (!isExtensible())
- goto reject;
+ return false;
} else {
// clause 4
Scope scope(engine);
@@ -752,12 +803,12 @@ bool Object::internalPut(String *name, const Value &value)
if (!memberIndex.isNull()) {
if (attrs.isAccessor()) {
if (!memberIndex->as<FunctionObject>())
- goto reject;
+ return false;
} else if (!isExtensible() || !attrs.isWritable()) {
- goto reject;
+ return false;
}
} else if (!isExtensible()) {
- goto reject;
+ return false;
}
}
@@ -769,24 +820,15 @@ bool Object::internalPut(String *name, const Value &value)
Scope scope(engine);
ScopedFunctionObject setter(scope, *memberIndex);
- ScopedCallData callData(scope, 1);
- callData->args[0] = value;
- callData->thisObject = this;
- setter->call(scope, callData);
- return !internalClass()->engine->hasException;
+ JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = value;
+ *jsCallData->thisObject = this;
+ setter->call(jsCallData);
+ return !engine->hasException;
}
insertMember(name, value);
return true;
-
- reject:
- // ### this should be removed once everything is ported to use Object::set()
- if (engine->current->strictMode) {
- QString message = QLatin1String("Cannot assign to read-only property \"") +
- name->toQString() + QLatin1Char('\"');
- engine->throwTypeError(message);
- }
- return false;
}
bool Object::internalPutIndexed(uint index, const Value &value)
@@ -802,7 +844,7 @@ bool Object::internalPutIndexed(uint index, const Value &value)
if (arrayIndex.isNull() && isStringObject()) {
if (index < static_cast<StringObject *>(this)->length())
// not writable
- goto reject;
+ return false;
}
// clause 1
@@ -810,15 +852,15 @@ bool Object::internalPutIndexed(uint index, const Value &value)
if (attrs.isAccessor()) {
if (arrayIndex->as<FunctionObject>())
goto cont;
- goto reject;
+ return false;
} else if (!attrs.isWritable())
- goto reject;
+ return false;
arrayIndex.set(engine, value);
return true;
} else if (!prototype()) {
if (!isExtensible())
- goto reject;
+ return false;
} else {
// clause 4
Scope scope(engine);
@@ -826,12 +868,12 @@ bool Object::internalPutIndexed(uint index, const Value &value)
if (!arrayIndex.isNull()) {
if (attrs.isAccessor()) {
if (!arrayIndex->as<FunctionObject>())
- goto reject;
+ return false;
} else if (!isExtensible() || !attrs.isWritable()) {
- goto reject;
+ return false;
}
} else if (!isExtensible()) {
- goto reject;
+ return false;
}
}
@@ -843,21 +885,15 @@ bool Object::internalPutIndexed(uint index, const Value &value)
Scope scope(engine);
ScopedFunctionObject setter(scope, *arrayIndex);
- ScopedCallData callData(scope, 1);
- callData->args[0] = value;
- callData->thisObject = this;
- setter->call(scope, callData);
- return !internalClass()->engine->hasException;
+ JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = value;
+ *jsCallData->thisObject = this;
+ setter->call(jsCallData);
+ return !engine->hasException;
}
arraySet(index, value);
return true;
-
- reject:
- // ### this should be removed once everything is ported to use Object::setIndexed()
- if (engine->current->strictMode)
- engine->throwTypeError();
- return false;
}
// Section 8.12.7
@@ -878,8 +914,6 @@ bool Object::internalDeleteProperty(String *name)
InternalClass::removeMember(this, name->identifier());
return true;
}
- if (engine()->current->strictMode)
- engine()->throwTypeError();
return false;
}
@@ -896,8 +930,6 @@ bool Object::internalDeleteIndexedProperty(uint index)
if (!ad || ad->vtable()->del(this, index))
return true;
- if (engine()->current->strictMode)
- engine()->throwTypeError();
return false;
}
@@ -921,7 +953,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs))
return true;
if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
- goto reject;
+ return false;
bool succeeded = true;
if (attrs.type() == PropertyAttributes::Data) {
bool ok;
@@ -938,7 +970,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
InternalClass::changeMember(this, engine->id_length(), cattrs);
}
if (!succeeded)
- goto reject;
+ return false;
return true;
}
@@ -948,7 +980,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
if (memberIndex == UINT_MAX) {
// clause 3
if (!isExtensible())
- goto reject;
+ return false;
// clause 4
ScopedProperty pd(scope);
pd->copy(p, attrs);
@@ -958,26 +990,18 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
}
return __defineOwnProperty__(engine, memberIndex, name, p, attrs);
-reject:
- if (engine->current->strictMode)
- engine->throwTypeError();
- return false;
}
bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
{
// 15.4.5.1, 4b
if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
- goto reject;
+ return false;
if (ArgumentsObject::isNonStrictArgumentsObject(this))
return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs);
return defineOwnProperty2(engine, index, p, attrs);
-reject:
- if (engine->current->strictMode)
- engine->throwTypeError();
- return false;
}
bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
@@ -994,7 +1018,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope
if (!hasProperty) {
// clause 3
if (!isExtensible())
- goto reject;
+ return false;
// clause 4
Scope scope(engine);
ScopedProperty pp(scope);
@@ -1010,10 +1034,6 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope
}
return __defineOwnProperty__(engine, index, 0, p, attrs);
-reject:
- if (engine->current->strictMode)
- engine->throwTypeError();
- return false;
}
bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs)
@@ -1040,9 +1060,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
// clause 7
if (!cattrs.isConfigurable()) {
if (attrs.isConfigurable())
- goto reject;
+ return false;
if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable())
- goto reject;
+ return false;
}
// clause 8
@@ -1053,7 +1073,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
if (cattrs.isData() != attrs.isData()) {
// 9a
if (!cattrs.isConfigurable())
- goto reject;
+ return false;
if (cattrs.isData()) {
// 9b
cattrs.setType(PropertyAttributes::Accessor);
@@ -1079,15 +1099,15 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
} else if (cattrs.isData() && attrs.isData()) { // clause 10
if (!cattrs.isConfigurable() && !cattrs.isWritable()) {
if (attrs.isWritable() || !current->value.sameValue(p->value))
- goto reject;
+ return false;
}
} else { // clause 10
Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor());
if (!cattrs.isConfigurable()) {
if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue())
- goto reject;
+ return false;
if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue())
- goto reject;
+ return false;
}
}
@@ -1102,10 +1122,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
arrayData()->setProperty(scope.engine, index, current);
}
return true;
- reject:
- if (engine->current->strictMode)
- engine->throwTypeError();
- return false;
}
@@ -1260,7 +1276,7 @@ void Heap::ArrayObject::init(const QStringList &list)
ReturnedValue ArrayObject::getLookup(const Managed *m, Lookup *l)
{
Scope scope(static_cast<const Object *>(m)->engine());
- ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
if (name->equals(scope.engine->id_length())) {
// special case, as the property is on the object itself
l->getter = Lookup::arrayLengthGetter;
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 066a93cc61..ff3ad7c0c6 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -57,6 +57,7 @@
#include "qv4scopedvalue_p.h"
#include "qv4value_p.h"
#include "qv4internalclass_p.h"
+#include "qv4string_p.h"
QT_BEGIN_NAMESPACE
@@ -71,11 +72,14 @@ namespace Heap {
Member(class, Pointer, MemberData *, memberData) \
Member(class, Pointer, ArrayData *, arrayData)
-DECLARE_HEAP_OBJECT(Object, Base) {
- DECLARE_MARK_TABLE(Object);
+DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
+ static void markObjects(Heap::Base *base, MarkStack *stack);
void init() { Base::init(); }
- void destroy() { Base::destroy(); }
+ const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
+ Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties);
+ return reinterpret_cast<const Value *>(this) + indexWithOffset;
+ }
const Value *inlinePropertyData(uint index) const {
Q_ASSERT(index < vtable()->nInlineProperties);
return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
@@ -128,25 +132,8 @@ DECLARE_HEAP_OBJECT(Object, Base) {
Heap::Object *prototype() const { return internalClass->prototype; }
};
-Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4)));
-
}
-#define V4_OBJECT(superClass) \
- public: \
- Q_MANAGED_CHECK \
- typedef superClass SuperClass; \
- static const QV4::ObjectVTable static_vtbl; \
- static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \
- V4_MANAGED_SIZE_TEST \
- Data *d_unchecked() const { return static_cast<Data *>(m()); } \
- Data *d() const { \
- Data *dptr = d_unchecked(); \
- dptr->_checkIsInitialized(); \
- return dptr; \
- } \
- V4_ASSERT_IS_TRIVIAL(Data);
-
#define V4_OBJECT2(DataClass, superClass) \
private: \
DataClass() Q_DECL_EQ_DELETE; \
@@ -164,8 +151,7 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4)));
dptr->_checkIsInitialized(); \
return dptr; \
} \
- V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); \
- static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable;
+ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass);
#define V4_PROTOTYPE(p) \
static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
@@ -174,8 +160,8 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4)));
struct ObjectVTable
{
VTable vTable;
- void (*call)(const Managed *, Scope &scope, CallData *data);
- void (*construct)(const Managed *, Scope &scope, CallData *data);
+ ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc);
ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty);
ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty);
bool (*put)(Managed *, String *name, const Value &value);
@@ -185,7 +171,7 @@ struct ObjectVTable
bool (*deleteProperty)(Managed *m, String *name);
bool (*deleteIndexedProperty)(Managed *m, uint index);
ReturnedValue (*getLookup)(const Managed *m, Lookup *l);
- void (*setLookup)(Managed *m, Lookup *l, const Value &v);
+ bool (*setLookup)(Managed *m, Lookup *l, const Value &v);
uint (*getLength)(const Managed *m);
void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var);
@@ -196,7 +182,7 @@ const QV4::ObjectVTable classname::static_vtbl = \
{ \
DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \
call, \
- construct, \
+ callAsConstructor, \
get, \
getIndexed, \
put, \
@@ -228,6 +214,8 @@ struct Q_QML_EXPORT Object: Managed {
V4_INTERNALCLASS(Object)
V4_PROTOTYPE(objectPrototype)
+ enum { NInlineProperties = 2 };
+
enum {
IsObject = true,
GetterOffset = 0,
@@ -287,12 +275,20 @@ struct Q_QML_EXPORT Object: Managed {
insertMember(name, value, Attr_Data|Attr_NotEnumerable);
}
void defineDefaultProperty(const QString &name, const Value &value);
- void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
- void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
- void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
- void (*setter)(const BuiltinFunction *, Scope &, CallData *));
- void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
- void (*setter)(const BuiltinFunction *, Scope &, CallData *));
+ // old calling convention
+ void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0);
+ void defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0);
+ // new calling convention
+ void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
+ void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
+ void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *),
+ ReturnedValue (*setter)(const BuiltinFunction *, CallData *));
+ void defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *),
+ ReturnedValue (*setter)(const BuiltinFunction *, CallData *));
+ void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
+ ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
+ void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
+ ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
/* Fixed: Writable: false, Enumerable: false, Configurable: false */
void defineReadonlyProperty(const QString &name, const Value &value);
void defineReadonlyProperty(String *name, const Value &value);
@@ -314,6 +310,8 @@ struct Q_QML_EXPORT Object: Managed {
// Array handling
public:
+ static void markObjects(Heap::Base *base, MarkStack *stack);
+
void copyArrayData(Object *other);
bool setArrayLength(uint newLen);
@@ -408,19 +406,6 @@ public:
return ret;
}
- inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow)
- {
- bool ret = vtable()->putIndexed(this, idx, v);
- if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
- ExecutionEngine *e = engine();
- if (!e->hasException) { // allow a custom set impl to throw itself
- e->throwTypeError();
- }
- }
- return ret;
- }
-
-
PropertyAttributes query(String *name) const
{ return vtable()->query(this, name); }
PropertyAttributes queryIndexed(uint index) const
@@ -431,21 +416,17 @@ public:
{ return vtable()->deleteIndexedProperty(this, index); }
ReturnedValue getLookup(Lookup *l) const
{ return vtable()->getLookup(this, l); }
- void setLookup(Lookup *l, const Value &v)
- { vtable()->setLookup(this, l, v); }
+ bool setLookup(Lookup *l, const Value &v)
+ { return vtable()->setLookup(this, l, v); }
void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{ vtable()->advanceIterator(this, it, name, index, p, attributes); }
uint getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); }
- inline void construct(Scope &scope, CallData *d) const
- { return vtable()->construct(this, scope, d); }
- inline void call(Scope &scope, CallData *d) const
- { vtable()->call(this, scope, d); }
protected:
- static void construct(const Managed *m, Scope &scope, CallData *);
- static void call(const Managed *m, Scope &scope, CallData *);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
static bool put(Managed *m, String *name, const Value &value);
@@ -455,11 +436,10 @@ protected:
static bool deleteProperty(Managed *m, String *name);
static bool deleteIndexedProperty(Managed *m, uint index);
static ReturnedValue getLookup(const Managed *m, Lookup *l);
- static void setLookup(Managed *m, Lookup *l, const Value &v);
+ static bool setLookup(Managed *m, Lookup *l, const Value &v);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static uint getLength(const Managed *m);
static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
- static void markObjects(Heap::Base *, MarkStack *);
private:
ReturnedValue internalGet(String *name, bool *hasProperty) const;
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 3427ee89fe..0394c704f9 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -177,9 +177,9 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString()
DEFINE_OBJECT_VTABLE(ForEachIteratorObject);
-void ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
+void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
- ForEachIteratorObject::Data *o = static_cast<ForEachIteratorObject::Data *>(that);
+ ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 6168d59914..30a6ad3025 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -116,6 +116,7 @@ struct ForEachIteratorObject : Object {
ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
private:
ObjectIteratorData itData;
};
@@ -127,9 +128,6 @@ struct ForEachIteratorObject: Object {
Q_MANAGED_TYPE(ForeachIteratorObject)
ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); }
-
-protected:
- static void markObjects(Heap::Base *that, MarkStack *markStack);
};
inline
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 2e72c0f13f..5eef757ce9 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -46,6 +46,7 @@
#include "qv4runtime_p.h"
#include "qv4objectiterator_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
@@ -60,27 +61,29 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Object"));
}
-void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that);
- if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) {
+ ExecutionEngine *v4 = f->engine();
+ const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f);
+ if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
+ Scope scope(v4);
ScopedObject obj(scope, scope.engine->newObject());
ScopedObject proto(scope, ctor->get(scope.engine->id_prototype()));
if (!!proto)
obj->setPrototype(proto);
- scope.result = obj.asReturnedValue();
+ return obj.asReturnedValue();
} else {
- scope.result = callData->args[0].toObject(scope.engine);
+ return argv[0].toObject(v4)->asReturnedValue();
}
}
-void ObjectCtor::call(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
- ExecutionEngine *v4 = scope.engine;
- if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) {
- scope.result = v4->newObject()->asReturnedValue();
+ ExecutionEngine *v4 = m->engine();
+ if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
+ return v4->newObject()->asReturnedValue();
} else {
- scope.result = callData->args[0].toObject(v4);
+ return argv[0].toObject(v4)->asReturnedValue();
}
}
@@ -123,58 +126,78 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable);
}
-void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (argc < 1)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
ScopedObject p(scope, o->prototype());
- scope.result = !!p ? p->asReturnedValue() : Encode::null();
+ return (!!p ? p->asReturnedValue() : Encode::null());
}
-void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject O(scope, callData->args[0].toObject(scope.engine));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (argc < 1)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
if (ArgumentsObject::isNonStrictArgumentsObject(O))
static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate();
- ScopedValue v(scope, callData->argument(1));
+ ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
ScopedString name(scope, v->toString(scope.engine));
- CHECK_EXCEPTION();
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
PropertyAttributes attrs;
ScopedProperty desc(scope);
O->getOwnProperty(name, &attrs, desc);
- scope.result = fromPropertyDescriptor(scope.engine, desc, attrs);
+ return fromPropertyDescriptor(scope.engine, desc, attrs);
}
-void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject O(scope, callData->args[0].toObject(scope.engine));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (argc < 1)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- scope.result = getOwnPropertyNames(scope.engine, callData->args[0]);
+ return Encode(getOwnPropertyNames(scope.engine, argv[0]));
}
// 19.1.2.1
-void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject to(scope, callData->args[0].toObject(scope.engine));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (argc < 1)
+ return scope.engine->throwTypeError();
- if (callData->argc == 1) {
- scope.result = to;
- return;
- }
+ ScopedObject to(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- for (int i = 1; i < callData->argc; ++i) {
- if (callData->args[i].isUndefined() || callData->args[i].isNull())
+ if (argc == 1)
+ return to.asReturnedValue();
+
+ for (int i = 1, ei = argc; i < ei; ++i) {
+ if (argv[i].isUndefined() || argv[i].isNull())
continue;
- ScopedObject from(scope, callData->args[i].toObject(scope.engine));
- CHECK_EXCEPTION();
+ ScopedObject from(scope, argv[i].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from));
quint32 length = keys->getLength();
@@ -195,64 +218,71 @@ void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallD
propValue = from->get(nextKey);
to->set(nextKey, propValue, Object::DoThrowOnRejection);
- CHECK_EXCEPTION();
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
}
}
- scope.result = to;
+ return to.asReturnedValue();
}
-void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
- ScopedValue O(scope, callData->argument(0));
- if (!O->isObject() && !O->isNull()) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ Scope scope(builtin);
+ if (!argc || (!argv[0].isObject() && !argv[0].isNull()))
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0]);
ScopedObject newObject(scope, scope.engine->newObject());
- newObject->setPrototype(O->as<Object>());
+ newObject->setPrototype(O);
- if (callData->argc > 1 && !callData->args[1].isUndefined()) {
- callData->args[0] = newObject;
- method_defineProperties(builtin, scope, callData);
- return;
+
+ if (argc > 1 && !argv[1].isUndefined()) {
+ Value *arguments = scope.alloc(argc);
+ arguments[0] = newObject;
+ memcpy(arguments + 1, argv + 1, (argc - 1)*sizeof(Value));
+ return method_defineProperties(builtin, thisObject, arguments, argc);
}
- scope.result = newObject;
+ return newObject.asReturnedValue();
}
-void ObjectPrototype::method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject O(scope, callData->argument(0));
- if (!O) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ Scope scope(b);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
- ScopedString name(scope, callData->argument(1), ScopedString::Convert);
- CHECK_EXCEPTION();
+ ScopedObject O(scope, argv[0]);
+ ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- ScopedValue attributes(scope, callData->argument(2));
+ ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
ScopedProperty pd(scope);
PropertyAttributes attrs;
toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
- CHECK_EXCEPTION();
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs))
THROW_TYPE_ERROR();
- scope.result = O;
+ return O.asReturnedValue();
}
-void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject O(scope, callData->argument(0));
- if (!O)
- THROW_TYPE_ERROR();
+ Scope scope(b);
+ if (argc < 2 || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0]);
- ScopedObject o(scope, callData->argument(1), ScopedObject::Convert);
- CHECK_EXCEPTION();
+ ScopedObject o(scope, argv[1].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
ScopedValue val(scope);
@@ -269,7 +299,8 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc
PropertyAttributes nattrs;
val = o->getValue(pd->value, attrs);
toPropertyDescriptor(scope.engine, val, n, &nattrs);
- CHECK_EXCEPTION();
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
bool ok;
if (name)
ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs);
@@ -279,18 +310,18 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc
THROW_TYPE_ERROR();
}
- scope.result = O;
+ return O.asReturnedValue();
}
-void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->argument(0));
- if (!o) {
+ const Value a = argc ? argv[0] : Primitive::undefinedValue();
+ if (!a.isObject())
// 19.1.2.17, 1
- scope.result = callData->argument(0);
- return;
- }
+ return a.asReturnedValue();
+ Scope scope(b);
+ ScopedObject o(scope, a);
o->setInternalClass(o->internalClass()->sealed());
if (o->arrayData()) {
@@ -301,17 +332,18 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat
}
}
- scope.result = o;
+ return o.asReturnedValue();
}
-void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->argument(0));
- if (!o) {
+ const Value a = argc ? argv[0] : Primitive::undefinedValue();
+ if (!a.isObject())
// 19.1.2.5, 1
- scope.result = callData->argument(0);
- return;
- }
+ return a.asReturnedValue();
+
+ Scope scope(b);
+ ScopedObject o(scope, a);
if (ArgumentsObject::isNonStrictArgumentsObject(o))
static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate();
@@ -327,116 +359,109 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD
o->arrayData()->attrs[i].setWritable(false);
}
}
- scope.result = o;
+ return o.asReturnedValue();
}
-void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- if (!o) {
- scope.result = callData->argument(0);
- return;
- }
+ Scope scope(b);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (!o)
+ return argv[0].asReturnedValue();
o->setInternalClass(o->internalClass()->nonExtensible());
- scope.result = o;
+ return o.asReturnedValue();
}
-void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- if (!o) {
- scope.result = Encode(true);
- return;
- }
+ Scope scope(b);
+ if (!argc)
+ return scope.engine->throwTypeError();
- if (o->isExtensible()) {
- scope.result = Encode(false);
- return;
- }
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (!o)
+ return Encode(true);
- if (o->internalClass() != o->internalClass()->sealed()) {
- scope.result = Encode(false);
- return;
- }
+ if (o->isExtensible())
+ return Encode(false);
- if (!o->arrayData() || !o->arrayData()->length()) {
- scope.result = Encode(true);
- return;
- }
+ if (o->internalClass() != o->internalClass()->sealed())
+ return Encode(false);
+
+ if (!o->arrayData() || !o->arrayData()->length())
+ return Encode(true);
Q_ASSERT(o->arrayData() && o->arrayData()->length());
- if (!o->arrayData()->attrs) {
- scope.result = Encode(false);
- return;
- }
+ if (!o->arrayData()->attrs)
+ return Encode(false);
for (uint i = 0; i < o->arrayData()->values.alloc; ++i) {
if (!o->arrayData()->isEmpty(i))
- if (o->arrayData()->attributes(i).isConfigurable()) {
- scope.result = Encode(false);
- return;
- }
+ if (o->arrayData()->attributes(i).isConfigurable())
+ return Encode(false);
}
- scope.result = Encode(true);
+ return Encode(true);
}
-void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- if (!o) {
- scope.result = Encode(true);
- return;
- }
+ Scope scope(b);
+ if (!argc)
+ return scope.engine->throwTypeError();
- if (o->isExtensible()) {
- scope.result = Encode(false);
- return;
- }
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (!o)
+ return Encode(true);
- if (o->internalClass() != o->internalClass()->frozen()) {
- scope.result = Encode(false);
- return;
- }
+ if (o->isExtensible())
+ return Encode(false);
- if (!o->arrayData() || !o->arrayData()->length()) {
- scope.result = Encode(true);
- return;
- }
+ if (o->internalClass() != o->internalClass()->frozen())
+ return Encode(false);
+
+ if (!o->arrayData() || !o->arrayData()->length())
+ return Encode(true);
Q_ASSERT(o->arrayData() && o->arrayData()->length());
- if (!o->arrayData()->attrs) {
- scope.result = Encode(false);
- return;
- }
+ if (!o->arrayData()->attrs)
+ return Encode(false);
for (uint i = 0; i < o->arrayData()->values.alloc; ++i) {
if (!o->arrayData()->isEmpty(i))
- if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) {
- scope.result = Encode(false);
- return;
- }
+ if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable())
+ return Encode(false);
}
- scope.result = Encode(true);
+ return Encode(true);
}
-void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_isExtensible(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- if (!o) {
- scope.result = Encode(false);
- return;
- }
+ Scope scope(b);
+ if (!argc)
+ return scope.engine->throwTypeError();
- scope.result = Encode((bool)o->isExtensible());
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (!o)
+ return Encode(false);
+
+ return Encode((bool)o->isExtensible());
}
-void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
ScopedArrayObject a(scope, scope.engine->newArrayObject());
@@ -449,101 +474,109 @@ void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallDat
a->push_back(name);
}
- scope.result = a;
+ return a.asReturnedValue();
}
-void ObjectPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- if (callData->thisObject.isUndefined()) {
- scope.result = scope.engine->newString(QStringLiteral("[object Undefined]"));
- } else if (callData->thisObject.isNull()) {
- scope.result = scope.engine->newString(QStringLiteral("[object Null]"));
+ ExecutionEngine *v4 = b->engine();
+ if (thisObject->isUndefined()) {
+ return Encode(v4->newString(QStringLiteral("[object Undefined]")));
+ } else if (thisObject->isNull()) {
+ return Encode(v4->newString(QStringLiteral("[object Null]")));
} else {
- ScopedObject obj(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(v4);
+ ScopedObject obj(scope, thisObject->toObject(scope.engine));
QString className = obj->className();
- scope.result = scope.engine->newString(QStringLiteral("[object %1]").arg(className));
+ return Encode(v4->newString(QStringLiteral("[object %1]").arg(className)));
}
}
-void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->thisObject.toObject(scope.engine));
+ Scope scope(b);
+ ScopedObject o(scope, thisObject->toObject(scope.engine));
if (!o)
RETURN_UNDEFINED();
ScopedFunctionObject f(scope, o->get(scope.engine->id_toString()));
if (!f)
THROW_TYPE_ERROR();
- ScopedCallData cData(scope);
- cData->thisObject = o;
- f->call(scope, callData);
+
+ return f->call(thisObject, argv, argc);
}
-void ObjectPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- scope.result = callData->thisObject.toObject(scope.engine);
+ return Encode(thisObject->toObject(b->engine()));
}
-void ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedString P(scope, callData->argument(0), ScopedString::Convert);
- CHECK_EXCEPTION();
- ScopedObject O(scope, callData->thisObject, ScopedObject::Convert);
- CHECK_EXCEPTION();
+ Scope scope(b);
+ ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
bool r = O->hasOwnProperty(P);
if (!r)
r = !O->query(P).isEmpty();
- scope.result = Encode(r);
+ return Encode(r);
}
-void ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject V(scope, callData->argument(0));
- if (!V) {
- scope.result = Encode(false);
- return;
- }
-
- ScopedObject O(scope, callData->thisObject, ScopedObject::Convert);
- CHECK_EXCEPTION();
+ Scope scope(b);
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ ScopedObject V(scope, argv[0]);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
ScopedObject proto(scope, V->prototype());
while (proto) {
- if (O->d() == proto->d()) {
- scope.result = Encode(true);
- return;
- }
+ if (O->d() == proto->d())
+ return Encode(true);
proto = proto->prototype();
}
- scope.result = Encode(false);
+ return Encode(false);
}
-void ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedString p(scope, callData->argument(0), ScopedString::Convert);
- CHECK_EXCEPTION();
-
- ScopedObject o(scope, callData->thisObject, ScopedObject::Convert);
- CHECK_EXCEPTION();
+ Scope scope(b);
+ ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ ScopedObject o(scope, thisObject->toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
PropertyAttributes attrs;
o->getOwnProperty(p, &attrs);
- scope.result = Encode(attrs.isEnumerable());
+ return Encode(attrs.isEnumerable());
}
-void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- if (callData->argc < 2)
+ Scope scope(b);
+ if (argc < 2)
THROW_TYPE_ERROR();
- ScopedFunctionObject f(scope, callData->argument(1));
+ ScopedFunctionObject f(scope, argv[1]);
if (!f)
THROW_TYPE_ERROR();
- ScopedString prop(scope, callData->argument(0), ScopedString::Convert);
- CHECK_EXCEPTION();
+ ScopedString prop(scope, argv[0], ScopedString::Convert);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- ScopedObject o(scope, callData->thisObject);
+ ScopedObject o(scope, thisObject);
if (!o) {
- if (!callData->thisObject.isUndefined())
+ if (!thisObject->isUndefined())
RETURN_UNDEFINED();
o = scope.engine->globalObject;
}
@@ -551,25 +584,29 @@ void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope,
ScopedProperty pd(scope);
pd->value = f;
pd->set = Primitive::emptyValue();
- o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ if (!ok)
+ THROW_TYPE_ERROR();
RETURN_UNDEFINED();
}
-void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- if (callData->argc < 2)
+ Scope scope(b);
+ if (argc < 2)
THROW_TYPE_ERROR();
- ScopedFunctionObject f(scope, callData->argument(1));
+ ScopedFunctionObject f(scope, argv[1]);
if (!f)
THROW_TYPE_ERROR();
- ScopedString prop(scope, callData->argument(0), ScopedString::Convert);
- CHECK_EXCEPTION();
+ ScopedString prop(scope, argv[0], ScopedString::Convert);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- ScopedObject o(scope, callData->thisObject);
+ ScopedObject o(scope, thisObject);
if (!o) {
- if (!callData->thisObject.isUndefined())
+ if (!thisObject->isUndefined())
RETURN_UNDEFINED();
o = scope.engine->globalObject;
}
@@ -577,31 +614,35 @@ void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope,
ScopedProperty pd(scope);
pd->value = Primitive::emptyValue();
pd->set = f;
- o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ if (!ok)
+ THROW_TYPE_ERROR();
RETURN_UNDEFINED();
}
-void ObjectPrototype::method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ScopedObject o(scope, callData->thisObject.as<Object>());
+ Scope scope(b);
+ ScopedObject o(scope, thisObject->as<Object>());
if (!o)
THROW_TYPE_ERROR();
- scope.result = o->prototype();
+ return Encode(o->prototype());
}
-void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ScopedObject o(scope, callData->thisObject);
- if (!o || !callData->argc)
+ Scope scope(b);
+ ScopedObject o(scope, thisObject);
+ if (!o || !argc)
THROW_TYPE_ERROR();
- if (callData->args[0].isNull()) {
+ if (argv[0].isNull()) {
o->setPrototype(0);
RETURN_UNDEFINED();
}
- ScopedObject p(scope, callData->args[0]);
+ ScopedObject p(scope, argv[0]);
bool ok = false;
if (!!p) {
if (o->prototype() == p->d()) {
@@ -610,10 +651,8 @@ void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, Ca
ok = o->setPrototype(p);
}
}
- if (!ok) {
- scope.result = scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
- return;
- }
+ if (!ok)
+ return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
RETURN_UNDEFINED();
}
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index 44b54267f3..2b231d46ad 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -70,41 +70,41 @@ struct ObjectCtor: FunctionObject
{
V4_OBJECT2(ObjectCtor, FunctionObject)
- static void construct(const Managed *that, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
};
struct ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_seal(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_keys(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hasOwnProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_propertyIsEnumerable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_defineGetter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineSetter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_get_proto(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set_proto(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static void toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs);
static ReturnedValue fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs);
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index 6b2369e795..8a24c71815 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -59,9 +59,6 @@
#if !QT_CONFIG(qml_debug)
-#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine)
-#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine)
-#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData))
QT_BEGIN_NAMESPACE
@@ -75,22 +72,6 @@ QT_END_NAMESPACE
#else
-#define Q_V4_PROFILE_ALLOC(engine, size, type)\
- (engine->profiler() &&\
- (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
- engine->profiler()->trackAlloc(size, type) : false)
-
-#define Q_V4_PROFILE_DEALLOC(engine, size, type) \
- (engine->profiler() &&\
- (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
- engine->profiler()->trackDealloc(size, type) : false)
-
-#define Q_V4_PROFILE(engine, function)\
- (Q_UNLIKELY(engine->profiler()) &&\
- (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\
- Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\
- function->code(engine, function->codeData))
-
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -270,19 +251,21 @@ public:
// It's enough to ref() the function in the destructor as it will probably not disappear while
// it's executing ...
- FunctionCallProfiler(Profiler *profiler, Function *function) :
- profiler(profiler), function(function), startTime(profiler->m_timer.nsecsElapsed())
- {}
-
- ~FunctionCallProfiler()
+ FunctionCallProfiler(ExecutionEngine *engine, Function *f)
+ : profiler(0)
{
- profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed()));
+ Profiler *p = engine->profiler();
+ if (Q_UNLIKELY(p) && (p->featuresEnabled & (1 << Profiling::FeatureFunctionCall))) {
+ profiler = p;
+ function = f;
+ startTime = profiler->m_timer.nsecsElapsed();
+ }
}
- static ReturnedValue profileCall(Profiler *profiler, ExecutionEngine *engine, Function *function)
+ ~FunctionCallProfiler()
{
- FunctionCallProfiler callProfiler(profiler, function);
- return function->code(engine, function->codeData);
+ if (profiler)
+ profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed()));
}
Profiler *profiler;
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 8b9ef149d6..5a165b2309 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -296,13 +296,8 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
outer.set(internalClass->engine, outerContext->d());
- strictMode = false;
- callData = outer->callData;
- lookups = outer->lookups;
- constantTable = outer->constantTable;
- compilationUnit = outer->compilationUnit;
- this->qml.set(internalClass->engine, qml->d());
+ this->activation.set(internalClass->engine, qml->d());
}
Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction)
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 48c9ee2c36..a1df5cb95f 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -76,12 +76,12 @@ struct QQmlContextWrapper : Object {
QQmlQPointer<QObject> scopeObject;
};
-#define QmlContextMembers(class, Member) \
- Member(class, Pointer, QQmlContextWrapper *, qml)
+#define QmlContextMembers(class, Member)
DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) {
- DECLARE_MARK_TABLE(QmlContext);
+ DECLARE_MARKOBJECTS(QmlContext);
+ QQmlContextWrapper *qml() { return static_cast<QQmlContextWrapper *>(activation.get()); }
void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml);
};
@@ -109,10 +109,10 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext
static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject);
QObject *qmlScope() const {
- return d()->qml->scopeObject;
+ return d()->qml()->scopeObject;
}
QQmlContextData *qmlContext() const {
- return *d()->qml->context;
+ return *d()->qml()->context;
}
};
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index fb3aa47d1b..6d7d929b61 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -62,6 +62,7 @@
#include <private/qv4regexpobject_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4mm_p.h>
#include <private/qqmlscriptstring_p.h>
#include <private/qv4compileddata_p.h>
@@ -468,11 +469,11 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) {
Q_ASSERT(!binding->isValueTypeProxy());
const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
- const auto stackFrame = engine->currentStackFrame();
+ const auto stackFrame = engine->currentStackFrame;
qCInfo(lcBindingRemoval,
"Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
object->metaObject()->className(), qPrintable(property->name(object)),
- qPrintable(stackFrame.source), stackFrame.line,
+ qPrintable(stackFrame->source()), stackFrame->lineNumber(),
qPrintable(qmlBinding->expressionIdentifier()));
}
}
@@ -815,18 +816,18 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
QV4::Scope scope(v4);
QV4::ScopedFunctionObject f(scope, This->function.value());
- QV4::ScopedCallData callData(scope, argCount);
- callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
+ QV4::JSCallData jsCallData(scope, argCount);
+ *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
for (int ii = 0; ii < argCount; ++ii) {
int type = argsTypes[ii + 1];
if (type == qMetaTypeId<QVariant>()) {
- callData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
+ jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
} else {
- callData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
+ jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
}
}
- f->call(scope, callData);
+ f->call(jsCallData);
if (scope.hasException()) {
QQmlError error = v4->catchExceptionAsQmlError();
if (error.description().isEmpty()) {
@@ -899,9 +900,11 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
} // namespace QV4
-void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QObjectWrapper::method_connect(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc == 0)
+ QV4::Scope scope(b);
+
+ if (callData->argc() == 0)
THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given");
QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject);
@@ -920,9 +923,9 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD
QV4::ScopedFunctionObject f(scope);
QV4::ScopedValue thisObject (scope, QV4::Encode::undefined());
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
f = callData->args[0];
- } else if (callData->argc >= 2) {
+ } else if (callData->argc() >= 2) {
thisObject = callData->args[0];
f = callData->args[1];
}
@@ -949,9 +952,11 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD
RETURN_UNDEFINED();
}
-void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QObjectWrapper::method_disconnect(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc == 0)
+ QV4::Scope scope(b);
+
+ if (callData->argc() == 0)
THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given");
QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject);
@@ -970,9 +975,9 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca
QV4::ScopedFunctionObject functionValue(scope);
QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined());
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
functionValue = callData->args[0];
- } else if (callData->argc >= 2) {
+ } else if (callData->argc() >= 2) {
functionThisValue = callData->args[0];
functionValue = callData->args[1];
}
@@ -1010,9 +1015,9 @@ static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markSt
}
}
-void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
+void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
{
- QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
+ QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QObject *o = This->object()) {
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
@@ -1027,7 +1032,7 @@ void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack)
markChildQObjectsRecursively(o, markStack);
}
- QV4::Object::markObjects(that, markStack);
+ Object::markObjects(that, markStack);
}
void QObjectWrapper::destroyObject(bool lastCall)
@@ -1412,7 +1417,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
+ QLatin1String(unknownTypeError));
}
- if (args[0] > callArgs->argc) {
+ if (args[0] > callArgs->argc()) {
QString error = QLatin1String("Insufficient arguments");
return engine->throwError(error);
}
@@ -1443,7 +1448,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
- int argumentCount = callArgs->argc;
+ int argumentCount = callArgs->argc();
QQmlPropertyData best;
int bestParameterScore = INT_MAX;
@@ -1848,7 +1853,7 @@ const QMetaObject *Heap::QObjectMethod::metaObject()
return object()->metaObject();
}
-QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const
+QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const
{
QString result;
if (const QMetaObject *metaObject = d()->metaObject()) {
@@ -1867,15 +1872,15 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co
result = QLatin1String("null");
}
- return ctx->engine()->newString(result)->asReturnedValue();
+ return engine->newString(result)->asReturnedValue();
}
-QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const
+QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const
{
if (!d()->object())
return Encode::undefined();
if (QQmlData::keepAliveDuringGarbageCollection(d()->object()))
- return ctx->engine()->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
+ return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
int delay = 0;
if (argc > 0)
@@ -1889,32 +1894,24 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con
return Encode::undefined();
}
-void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
- This->callInternal(callData, scope);
+ return This->callInternal(thisObject, argv, argc);
}
-void QObjectMethod::callInternal(CallData *callData, Scope &scope) const
+ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
{
ExecutionEngine *v4 = engine();
- ExecutionContext *context = v4->currentContext;
- if (d()->index == DestroyMethod) {
- scope.result = method_destroy(context, callData->args, callData->argc);
- return;
- }
-
- else if (d()->index == ToStringMethod) {
- scope.result = method_toString(context);
- return;
- }
+ if (d()->index == DestroyMethod)
+ return method_destroy(v4, argv, argc);
+ else if (d()->index == ToStringMethod)
+ return method_toString(v4);
QQmlObjectOrGadget object(d()->object());
if (!d()->object()) {
- if (!d()->valueTypeWrapper) {
- scope.result = Encode::undefined();
- return;
- }
+ if (!d()->valueTypeWrapper)
+ return Encode::undefined();
object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr);
}
@@ -1923,20 +1920,16 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const
if (d()->propertyCache()) {
QQmlPropertyData *data = d()->propertyCache()->method(d()->index);
- if (!data) {
- scope.result = QV4::Encode::undefined();
- return;
- }
+ if (!data)
+ return QV4::Encode::undefined();
method = *data;
} else {
const QMetaObject *mo = d()->object()->metaObject();
const QMetaMethod moMethod = mo->method(d()->index);
method.load(moMethod);
- if (method.coreIndex() == -1) {
- scope.result = QV4::Encode::undefined();
- return;
- }
+ if (method.coreIndex() == -1)
+ return QV4::Encode::undefined();
// Look for overloaded methods
QByteArray methodName = moMethod.name();
@@ -1951,21 +1944,25 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const
}
}
+ Scope scope(v4);
+ JSCallData cData(scope, argc, argv, thisObject);
+ CallData *callData = cData.callData();
+
if (method.isV4Function()) {
- scope.result = QV4::Encode::undefined();
- QQmlV4Function func(callData, &scope.result, v4);
+ QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue());
+ QQmlV4Function func(callData, rv, v4);
QQmlV4Function *funcptr = &func;
void *args[] = { 0, &funcptr };
object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args);
- return;
+ return rv->asReturnedValue();
}
if (!method.isOverload()) {
- scope.result = CallPrecise(object, method, v4, callData);
+ return CallPrecise(object, method, v4, callData);
} else {
- scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache());
+ return CallOverloaded(object, method, v4, callData, d()->propertyCache());
}
}
@@ -2028,13 +2025,14 @@ void QMetaObjectWrapper::init(ExecutionEngine *) {
}
}
-void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m);
- scope.result = This->constructInternal(callData);
+ const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
+ return This->constructInternal(argv, argc);
}
-ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const {
+ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const
+{
d()->ensureConstructorsCache();
@@ -2047,6 +2045,8 @@ ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const {
Scope scope(v4);
Scoped<QObjectWrapper> object(scope);
+ JSCallData cData(scope, argc, argv);
+ CallData *callData = cData.callData();
if (d()->constructorCount == 1) {
object = callConstructor(d()->constructors[0], v4, callData);
@@ -2071,7 +2071,7 @@ ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data,
ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
const int numberOfConstructors = d()->constructorCount;
- const int argumentCount = callArgs->argc;
+ const int argumentCount = callArgs->argc();
const QQmlStaticMetaObject object(d()->metaObject);
QQmlPropertyData best;
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 018e444f7c..c00e82e4fa 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -90,6 +90,7 @@ struct Q_QML_EXPORT QObjectWrapper : Object {
}
QObject *object() const { return qObj.data(); }
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
private:
QQmlQPointer<QObject> qObj;
@@ -102,7 +103,7 @@ private:
Member(class, NoMark, int, index)
DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
- DECLARE_MARK_TABLE(QObjectMethod);
+ DECLARE_MARKOBJECTS(QObjectMethod);
void init(QV4::ExecutionContext *scope);
void destroy()
@@ -196,10 +197,9 @@ protected:
static bool put(Managed *m, String *name, const Value &value);
static PropertyAttributes query(const Managed *, String *name);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static void markObjects(Heap::Base *that, QV4::MarkStack *markStack);
- static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_connect(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_disconnect(const BuiltinFunction *, CallData *callData);
private:
Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
@@ -234,12 +234,12 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }
- QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const;
- QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const;
+ QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const;
+ QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const;
- static void call(const Managed *, Scope &scope, CallData *callData);
+ static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- void callInternal(CallData *callData, Scope &scope) const;
+ ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
};
@@ -251,14 +251,14 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
V4_NEEDS_DESTROY
static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
- static void construct(const Managed *, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
static bool isEqualTo(Managed *a, Managed *b);
const QMetaObject *metaObject() const { return d()->metaObject; }
private:
void init(ExecutionEngine *engine);
- ReturnedValue constructInternal(CallData *callData) const;
+ ReturnedValue constructInternal(const Value *argv, int argc) const;
ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index 162f0fba57..fb49def317 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -62,7 +62,7 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
WTF::String s(string);
#if ENABLE(YARR_JIT)
- if (!jitCode()->isFallBack() && jitCode()->has16BitCode())
+ if (d()->hasValidJITCode())
return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start);
#endif
@@ -82,7 +82,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
Scope scope(engine);
- Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global));
+ Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(pattern, ignoreCase, multiline, global));
result->d()->cache = cache;
cachedValue.set(engine, result);
@@ -90,7 +90,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
}
-void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline, bool global)
+void Heap::RegExp::init(const QString &pattern, bool ignoreCase, bool multiline, bool global)
{
Base::init();
this->pattern = new QString(pattern);
@@ -98,20 +98,28 @@ void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ig
this->multiLine = multiline;
this->global = global;
+ valid = false;
+
const char* error = 0;
- JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error);
+ JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiLine, &error);
if (error)
return;
subPatternCount = yarrPattern.m_numSubpatterns;
- OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator);
- byteCode = p.take();
#if ENABLE(YARR_JIT)
- jitCode = new JSC::Yarr::YarrCodeBlock;
- if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) {
- JSC::JSGlobalData dummy(engine->regExpAllocator);
+ if (!yarrPattern.m_containsBackreferences) {
+ jitCode = new JSC::Yarr::YarrCodeBlock;
+ JSC::JSGlobalData dummy(internalClass->engine->regExpAllocator);
JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode);
}
#endif
+ if (hasValidJITCode()) {
+ valid = true;
+ return;
+ }
+ OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator);
+ byteCode = p.take();
+ if (byteCode)
+ valid = true;
}
void Heap::RegExp::destroy()
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index 998f6e3da3..498468e165 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -76,7 +76,7 @@ struct RegExpCacheKey;
namespace Heap {
struct RegExp : Base {
- void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global);
+ void init(const QString& pattern, bool ignoreCase, bool multiline, bool global);
void destroy();
QString *pattern;
@@ -84,11 +84,19 @@ struct RegExp : Base {
#if ENABLE(YARR_JIT)
JSC::Yarr::YarrCodeBlock *jitCode;
#endif
+ bool hasValidJITCode() const {
+#if ENABLE(YARR_JIT)
+ return jitCode && !jitCode->isFallBack() && jitCode->has16BitCode();
+#else
+ return false;
+#endif
+ }
RegExpCache *cache;
int subPatternCount;
bool ignoreCase;
bool multiLine;
bool global;
+ bool valid;
int captureCount() const { return subPatternCount + 1; }
};
@@ -116,7 +124,7 @@ struct RegExp : public Managed
static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false);
- bool isValid() const { return d()->byteCode; }
+ bool isValid() const { return d()->valid; }
uint match(const QString& string, int start, uint *matchOffsets);
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 83bfe21c67..f95719b25f 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -38,20 +38,17 @@
****************************************************************************/
#include "qv4regexpobject_p.h"
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
#include "qv4objectproto_p.h"
#include "qv4regexp_p.h"
#include "qv4stringobject_p.h"
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
-#include <qv4jsir_p.h>
-#include <qv4codegen_p.h>
#include "private/qlocale_tools_p.h"
#include <QtCore/QDebug>
@@ -217,39 +214,33 @@ void Heap::RegExpCtor::clearLastMatch()
lastMatchEnd = 0;
}
-void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
{
- ScopedValue r(scope, callData->argument(0));
- ScopedValue f(scope, callData->argument(1));
+ Scope scope(fo->engine());
+ ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
Scoped<RegExpObject> re(scope, r);
if (re) {
- if (!f->isUndefined()) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ if (!f->isUndefined())
+ return scope.engine->throwTypeError();
Scoped<RegExp> regexp(scope, re->value());
- scope.result = Encode(scope.engine->newRegExpObject(regexp));
- return;
+ return Encode(scope.engine->newRegExpObject(regexp));
}
QString pattern;
if (!r->isUndefined())
pattern = r->toQString();
- if (scope.hasException()) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.hasException())
+ return Encode::undefined();
bool global = false;
bool ignoreCase = false;
bool multiLine = false;
if (!f->isUndefined()) {
ScopedString s(scope, f->toString(scope.engine));
- if (scope.hasException()) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.hasException())
+ return Encode::undefined();
QString str = s->toQString();
for (int i = 0; i < str.length(); ++i) {
if (str.at(i) == QLatin1Char('g') && !global) {
@@ -259,31 +250,27 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData)
} else if (str.at(i) == QLatin1Char('m') && !multiLine) {
multiLine = true;
} else {
- scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
- return;
+ return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
}
}
}
Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global));
if (!regexp->isValid()) {
- scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
- return;
+ return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
}
- scope.result = Encode(scope.engine->newRegExpObject(regexp));
+ return Encode(scope.engine->newRegExpObject(regexp));
}
-void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) {
- if (callData->argc == 1 || callData->args[1].isUndefined()) {
- scope.result = callData->args[0];
- return;
- }
+ if (argc > 0 && argv[0].as<RegExpObject>()) {
+ if (argc == 1 || argv[1].isUndefined())
+ return Encode(argv[0]);
}
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
@@ -323,13 +310,60 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
defineDefaultProperty(QStringLiteral("compile"), method_compile, 2);
}
-void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallData *callData)
+/* used by String.match */
+ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
+ Q_ASSERT(r && r->global());
+
+ ScopedString str(scope, argc ? argv[0] : Primitive::undefinedValue());
+ Q_ASSERT(str);
+ QString s = str->toQString();
+
+ int offset = r->lastIndex();
+ if (offset < 0 || offset > s.length()) {
+ r->setLastIndex(0);
+ RETURN_RESULT(Encode::null());
+ }
+
+ Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint));
+ const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
+
+ RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
+ regExpCtor->d()->clearLastMatch();
+
+ if (result == -1) {
+ r->setLastIndex(0);
+ RETURN_RESULT(Encode::null());
+ }
+
+ ReturnedValue retVal = Encode::undefined();
+ // return first match
+ if (r->value()->captureCount()) {
+ int start = matchOffsets[0];
+ int end = matchOffsets[1];
+ retVal = (start != -1) ? scope.engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined();
+ }
+
+ RegExpCtor::Data *dd = regExpCtor->d();
+ dd->lastInput.set(scope.engine, str->d());
+ dd->lastMatchStart = matchOffsets[0];
+ dd->lastMatchEnd = matchOffsets[1];
+
+ r->setLastIndex(matchOffsets[1]);
+
+ return retVal;
+}
+
+ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>());
+ Scope scope(b);
+ Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
if (!r)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
- ScopedValue arg(scope, callData->argument(0));
+ ScopedValue arg(scope, argc ? argv[0]: Primitive::undefinedValue());
ScopedString str(scope, arg->toString(scope.engine));
if (scope.hasException())
RETURN_UNDEFINED();
@@ -344,7 +378,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat
Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint));
const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
- Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor());
+ RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
regExpCtor->d()->clearLastMatch();
if (result == -1) {
@@ -376,73 +410,78 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat
if (r->global())
r->setLastIndex(matchOffsets[1]);
- scope.result = array;
+ return array.asReturnedValue();
}
-void RegExpPrototype::method_test(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- method_exec(b, scope, callData);
- scope.result = Encode(!scope.result.isNull());
+ Value res = Value::fromReturnedValue(method_exec(b, thisObject, argv, argc));
+ return Encode(!res.isNull());
}
-void RegExpPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>());
+ ExecutionEngine *v4 = b->engine();
+ const RegExpObject *r = thisObject->as<RegExpObject>();
if (!r)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = scope.engine->newString(r->toString());
+ return Encode(v4->newString(r->toString()));
}
-void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>());
+ Scope scope(b);
+ Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
if (!r)
- THROW_TYPE_ERROR();
-
- ScopedCallData cData(scope, callData->argc);
- memcpy(cData->args, callData->args, callData->argc*sizeof(Value));
+ return scope.engine->throwTypeError();
- scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData);
- Scoped<RegExpObject> re(scope, scope.result.asReturnedValue());
+ Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc));
r->d()->value.set(scope.engine, re->value());
+ return Encode::undefined();
}
template <int index>
-void RegExpPrototype::method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int)
{
+ Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- scope.result = lastMatch ? lastMatch->getIndexed(index) : Encode::undefined();
- if (scope.result.isUndefined())
- scope.result = scope.engine->newString();
+ ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined());
+ if (res->isUndefined())
+ res = scope.engine->newString();
+ return res->asReturnedValue();
}
-void RegExpPrototype::method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, const Value *, const Value *, int)
{
+ Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- scope.result = lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined();
- if (scope.result.isUndefined())
- scope.result = scope.engine->newString();
+ ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined());
+ if (res->isUndefined())
+ res = scope.engine->newString();
+ return res->asReturnedValue();
}
-void RegExpPrototype::method_get_input(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue RegExpPrototype::method_get_input(const FunctionObject *b, const Value *, const Value *, int)
{
- scope.result = static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastInput();
+ return static_cast<RegExpCtor*>(b->engine()->regExpCtor())->lastInput()->asReturnedValue();
}
-void RegExpPrototype::method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue RegExpPrototype::method_get_leftContext(const FunctionObject *b, const Value *, const Value *, int)
{
+ Scope scope(b);
Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor());
QString lastInput = regExpCtor->lastInput()->toQString();
- scope.result = scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart()));
+ return Encode(scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart())));
}
-void RegExpPrototype::method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue RegExpPrototype::method_get_rightContext(const FunctionObject *b, const Value *, const Value *, int)
{
+ Scope scope(b);
Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor());
QString lastInput = regExpCtor->lastInput()->toQString();
- scope.result = scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()));
+ return Encode(scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd())));
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 65055ccc81..181628241b 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -55,8 +55,6 @@
#include "qv4context_p.h"
#include "qv4functionobject_p.h"
#include "qv4string_p.h"
-#include <qv4codegen_p.h>
-#include <qv4isel_p.h>
#include "qv4managed_p.h"
#include "qv4property_p.h"
#include "qv4objectiterator_p.h"
@@ -78,7 +76,7 @@ namespace Heap {
Member(class, Pointer, RegExp *, value)
DECLARE_HEAP_OBJECT(RegExpObject, Object) {
- DECLARE_MARK_TABLE(RegExpObject);
+ DECLARE_MARKOBJECTS(RegExpObject);
void init();
void init(QV4::RegExp *value);
@@ -92,7 +90,7 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) {
Member(class, NoMark, int, lastMatchEnd)
DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) {
- DECLARE_MARK_TABLE(RegExpCtor);
+ DECLARE_MARKOBJECTS(RegExpCtor);
void init(QV4::ExecutionContext *scope);
void clearLastMatch();
@@ -106,7 +104,7 @@ struct RegExpObject: Object {
V4_INTERNALCLASS(RegExpObject)
V4_PROTOTYPE(regExpPrototype)
- // needs to be compatible with the flags in qv4jsir_p.h
+ // needs to be compatible with the flags in qv4compileddata_p.h
enum Flags {
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
@@ -123,6 +121,8 @@ struct RegExpObject: Object {
Index_ArrayInput = Index_ArrayIndex + 1
};
+ enum { NInlineProperties = 5 };
+
Heap::RegExp *value() const { return d()->value; }
bool global() const { return d()->value->global; }
@@ -152,25 +152,27 @@ struct RegExpCtor: FunctionObject
int lastMatchStart() { return d()->lastMatchStart; }
int lastMatchEnd() { return d()->lastMatchEnd; }
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct RegExpPrototype: RegExpObject
{
void init(ExecutionEngine *engine, Object *ctor);
- static void method_exec(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_test(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_compile(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_exec(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_test(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
template <int index>
- static void method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_input(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_leftContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_rightContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 4b952bcbbc..c41b720610 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -55,6 +55,7 @@
#include "qv4regexpobject_p.h"
#include "private/qlocale_tools_p.h"
#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include <private/qv4qmlcontext_p.h>
#include <private/qqmltypewrapper_p.h>
#include <private/qqmlengine_p.h>
@@ -310,47 +311,47 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix)
ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
{
- QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeFunctions[functionId];
+ QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
Q_ASSERT(clos);
- return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue();
+ ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
}
-ReturnedValue Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
+bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
ScopedObject o(scope, base);
if (o) {
uint n = index.asArrayIndex();
- if (n < UINT_MAX) {
- return Encode((bool)o->deleteIndexedProperty(n));
- }
+ if (n < UINT_MAX)
+ return o->deleteIndexedProperty(n);
}
ScopedString name(scope, index.toString(engine));
return method_deleteMemberString(engine, base, name);
}
-ReturnedValue Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
+bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
return method_deleteMemberString(engine, base, name);
}
-ReturnedValue Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
+bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
{
Scope scope(engine);
ScopedObject obj(scope, base.toObject(engine));
if (scope.engine->hasException)
return Encode::undefined();
- return Encode(obj->deleteProperty(name));
+ return obj->deleteProperty(name);
}
-ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
+bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- return Encode(engine->currentContext->deleteProperty(name));
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name);
}
QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval)
@@ -425,14 +426,13 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
qSwap(meth1, meth2);
Scope scope(engine);
- ScopedCallData callData(scope, 0);
- callData->thisObject = *object;
-
+ ScopedValue result(scope);
ScopedValue conv(scope, object->get(meth1));
+
if (FunctionObject *o = conv->as<FunctionObject>()) {
- o->call(scope, callData);
- if (scope.result.isPrimitive())
- return scope.result.asReturnedValue();
+ result = o->call(object, nullptr, 0);
+ if (result->isPrimitive())
+ return result->asReturnedValue();
}
if (engine->hasException)
@@ -440,9 +440,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
conv = object->get(meth2);
if (FunctionObject *o = conv->as<FunctionObject>()) {
- o->call(scope, callData);
- if (scope.result.isPrimitive())
- return scope.result.asReturnedValue();
+ result = o->call(object, nullptr, 0);
+ if (result->isPrimitive())
+ return result->asReturnedValue();
}
return engine->throwTypeError();
@@ -469,8 +469,9 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val
}
}
-Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Value &value)
+Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value value, TypeHint hint)
{
+ redo:
switch (value.type()) {
case Value::Empty_Type:
Q_ASSERT(!"empty Value encountered");
@@ -484,15 +485,15 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val
return engine->id_true()->d();
else
return engine->id_false()->d();
- case Value::Managed_Type:
- if (String *s = value.stringValue())
- return s->d();
- {
- Scope scope(engine);
- ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, STRING_HINT));
- Q_ASSERT(!prim->isManaged() || prim->isString());
- return RuntimeHelpers::convertToString(engine, prim);
- }
+ case Value::Managed_Type: {
+ if (value.isString())
+ return static_cast<const String &>(value).d();
+ value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint));
+ Q_ASSERT(value.isPrimitive());
+ if (value.isString())
+ return static_cast<const String &>(value).d();
+ goto redo;
+ }
case Value::Integer_Type:
return RuntimeHelpers::stringFromNumber(engine, value.int_32());
default: // double
@@ -502,34 +503,9 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val
// This is slightly different from the method above, as
// the + operator requires a slightly different conversion
-static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value &value)
+static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value)
{
- switch (value.type()) {
- case Value::Empty_Type:
- Q_ASSERT(!"empty Value encountered");
- Q_UNREACHABLE();
- case Value::Undefined_Type:
- return engine->id_undefined()->d();
- case Value::Null_Type:
- return engine->id_null()->d();
- case Value::Boolean_Type:
- if (value.booleanValue())
- return engine->id_true()->d();
- else
- return engine->id_false()->d();
- case Value::Managed_Type:
- if (String *s = value.stringValue())
- return s->d();
- {
- Scope scope(engine);
- ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, PREFERREDTYPE_HINT));
- return RuntimeHelpers::convertToString(engine, prim);
- }
- case Value::Integer_Type:
- return RuntimeHelpers::stringFromNumber(engine, value.int_32());
- default: // double
- return RuntimeHelpers::stringFromNumber(engine, value.doubleValue());
- } // switch
+ return RuntimeHelpers::convertToString(engine, value, PREFERREDTYPE_HINT);
}
QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Value &left, const Value &right)
@@ -549,7 +525,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
pright = convert_to_string_add(engine, pright);
sright = static_cast<String *>(pright.ptr);
}
- if (scope.engine->hasException)
+ if (engine->hasException)
return Encode::undefined();
if (!sleft->d()->length())
return sright->asReturnedValue();
@@ -563,42 +539,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
return Encode(x + y);
}
-QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Value &left, const Value &right)
+bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
{
- Q_ASSERT(left.isString() || right.isString());
-
Scope scope(engine);
- ScopedValue pleft(scope, left);
- ScopedValue pright(scope, right);
- String *sleft = pleft->stringValue();
- String *sright = pright->stringValue();
-
- if (!sleft) {
- pleft = convert_to_string_add(engine, pleft);
- sleft = static_cast<String *>(pleft.ptr);
- }
- if (!sright) {
- pright = convert_to_string_add(engine, pright);
- sright = static_cast<String *>(pright.ptr);
- }
- if (scope.engine->hasException)
- return Encode::undefined();
- if (!sleft->d()->length())
- return pright->asReturnedValue();
- if (!sright->d()->length())
- return pleft->asReturnedValue();
- MemoryManager *mm = engine->memoryManager;
- return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue();
-}
-
-void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
ScopedObject o(scope, object.toObject(engine));
if (!o)
- return;
- o->put(name, value);
+ return false;
+ return o->put(name, value);
}
static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
@@ -656,7 +604,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine,
return o->get(name);
}
-ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index)
+ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index)
{
uint idx;
if (index.asArrayIndex(idx)) {
@@ -677,12 +625,12 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o
return getElementFallback(engine, object, index);
}
-static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
ScopedObject o(scope, object.toObject(engine));
if (engine->hasException)
- return;
+ return false;
uint idx;
if (index.asArrayIndex(idx)) {
@@ -690,18 +638,17 @@ static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Val
Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
s->setData(engine, idx, value);
- return;
+ return true;
}
}
- o->putIndexed(idx, value);
- return;
+ return o->putIndexed(idx, value);
}
ScopedString name(scope, index.toString(engine));
- o->put(name, value);
+ return o->put(name, value);
}
-void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
uint idx;
if (index.asArrayIndex(idx)) {
@@ -712,7 +659,7 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
s->setData(engine, idx, value);
- return;
+ return true;
}
}
}
@@ -742,17 +689,31 @@ ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_itera
}
-void Runtime::method_setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value)
+void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value)
+{
+ 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);
+
+ if (e == ExecutionContext::RangeError)
+ engine->globalObject->put(name, value);
+}
+
+void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- engine->currentContext->setProperty(name, value);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value);
+ if (e == ExecutionContext::TypeError)
+ engine->throwTypeError();
+ else if (e == ExecutionContext::RangeError)
+ engine->throwReferenceError(name);
}
-ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &object, int nameIndex)
+ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
ScopedObject o(scope, object);
if (o)
@@ -769,11 +730,11 @@ ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &
return o->get(name);
}
-ReturnedValue Runtime::method_getActivationProperty(ExecutionEngine *engine, int nameIndex)
+ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- return engine->currentContext->getProperty(name);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name);
}
#endif // V4_BOOTSTRAP
@@ -1002,233 +963,142 @@ uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const
}
-ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc)
{
- Scope scope(engine);
- Q_ASSERT(callData->thisObject.isUndefined());
-
- Lookup *l = engine->current->lookups + index;
- ScopedFunctionObject o(scope, l->globalGetter(l, engine));
- if (!o)
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
+ if (!function.isFunctionObject())
return engine->throwTypeError();
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
- if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) {
- static_cast<EvalFunction *>(o.getPointer())->evalCall(scope, callData, true);
- } else {
- o->call(scope, callData);
- }
-
- return scope.result.asReturnedValue();
+ Value thisObject = Primitive::undefinedValue();
+ return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
-
-ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc)
{
- Q_ASSERT(callData->thisObject.isUndefined());
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedValue thisObject(scope);
- ScopedObject base(scope);
- ScopedValue func(scope, engine->currentContext->getPropertyAndBase(name, base.getRef()));
- if (scope.engine->hasException)
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject));
+ if (engine->hasException)
return Encode::undefined();
- if (base)
- callData->thisObject = base;
-
- FunctionObject *o = func->as<FunctionObject>();
- if (!o) {
+ if (!function) {
QString objectAsString = QStringLiteral("[null]");
- if (base)
- objectAsString = ScopedValue(scope, base.asReturnedValue())->toQStringNoThrow();
- QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString);
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
return engine->throwTypeError(msg);
}
- if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) {
- static_cast<EvalFunction *>(o)->evalCall(scope, callData, true);
- } else {
- o->call(scope, callData);
- }
+ if (function->d() == engine->evalFunction()->d())
+ return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
- return scope.result.asReturnedValue();
+ return function->call(thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
+ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
- ScopedFunctionObject o(scope, method_getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true));
- if (!o) {
- QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
+ ScopedValue thisObject(scope);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- o->call(scope, callData);
- return scope.result.asReturnedValue();
-}
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject));
+ if (engine->hasException)
+ return Encode::undefined();
-ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
-{
- Scope scope(engine);
- ScopedFunctionObject o(scope, method_getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true));
- if (!o) {
- QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
+ if (!f) {
+ QString objectAsString = QStringLiteral("[null]");
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ objectAsString);
+ return engine->throwTypeError(msg);
}
- o->call(scope, callData);
- return scope.result.asReturnedValue();
+ return f->call(thisObject, argv, argc);
}
-ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- ScopedObject baseObject(scope, callData->thisObject);
- if (!baseObject) {
- Q_ASSERT(!callData->thisObject.isEmpty());
- if (callData->thisObject.isNullOrUndefined()) {
- QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(callData->thisObject.toQStringNoThrow());
+
+ if (!base->isObject()) {
+ Q_ASSERT(!base->isEmpty());
+ if (base->isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ base->toQStringNoThrow());
return engine->throwTypeError(message);
}
- baseObject = RuntimeHelpers::convertToObject(scope.engine, callData->thisObject);
- if (!baseObject) // type error
+ ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base));
+ if (engine->hasException) // type error
return Encode::undefined();
- callData->thisObject = baseObject.asReturnedValue();
+ base = thisObject;
}
- ScopedFunctionObject o(scope, baseObject->get(name));
- if (o) {
- o->call(scope, callData);
- return scope.result.asReturnedValue();
- } else {
- QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow());
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name));
+
+ if (!f) {
+ QString error = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ base->toQStringNoThrow());
return engine->throwTypeError(error);
}
+ return f->call(base, argv, argc);
}
-ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
-{
- Lookup *l = engine->current->lookups + index;
- Value v;
- v = l->getter(l, engine, callData->thisObject);
- Object *o = v.objectValue();
- if (Q_LIKELY(o)) {
- Scope scope(engine);
- o->call(scope, callData);
- return scope.result.asReturnedValue();
- }
- return engine->throwTypeError();
-}
-
-ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData)
+ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)
{
- Scope scope(engine);
- ScopedObject baseObject(scope, callData->thisObject.toObject(engine));
- ScopedString s(scope, index.toString(engine));
-
- if (scope.engine->hasException)
- return Encode::undefined();
- callData->thisObject = baseObject;
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ // ok to have the value on the stack here
+ Value f = Value::fromReturnedValue(l->getter(l, engine, *base));
- ScopedObject o(scope, baseObject->get(s));
- if (!o)
+ if (!f.isFunctionObject())
return engine->throwTypeError();
- o->call(scope, callData);
- return scope.result.asReturnedValue();
-}
-
-ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData)
-{
- if (Object *o = func.objectValue()) {
- Scope scope(engine);
- o->call(scope, callData);
- return scope.result.asReturnedValue();
- }
- return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
+ return static_cast<FunctionObject &>(f).call(base, argv, argc);
}
-
-ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)
{
Scope scope(engine);
- Q_ASSERT(callData->thisObject.isUndefined());
-
- Lookup *l = engine->current->lookups + index;
- ScopedObject f(scope, l->globalGetter(l, engine));
- if (f) {
- f->construct(scope, callData);
- return scope.result.asReturnedValue();
- } else {
- return engine->throwTypeError();
- }
-}
-
+ ScopedValue thisObject(scope, base->toObject(engine));
+ base = thisObject;
-ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- ScopedValue func(scope, engine->currentContext->getProperty(name));
- if (scope.engine->hasException)
+ ScopedString str(scope, index.toString(engine));
+ if (engine->hasException)
return Encode::undefined();
- Object *f = func->as<Object>();
+ ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str));
if (!f)
return engine->throwTypeError();
- f->construct(scope, callData);
- return scope.result.asReturnedValue();
+ return f->call(base, argv, argc);
}
-ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData)
+ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc)
{
- const Object *f = func.as<Object>();
- if (!f)
- return engine->throwTypeError();
-
- Scope scope(engine);
- f->construct(scope, callData);
- return scope.result.asReturnedValue();
+ if (!func.isFunctionObject())
+ return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
+ return static_cast<const FunctionObject &>(func).call(nullptr, argv, argc);
}
-ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
-{
- Scope scope(engine);
- ScopedObject thisObject(scope, callData->thisObject.toObject(engine));
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- if (scope.engine->hasException)
- return Encode::undefined();
- ScopedObject f(scope, thisObject->get(name));
- if (f) {
- Scope scope(engine);
- f->construct(scope, callData);
- return scope.result.asReturnedValue();
- } else {
+ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc)
+{
+ if (!function.isFunctionObject())
return engine->throwTypeError();
- }
-}
-ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
-{
- Lookup *l = engine->current->lookups + index;
- Value v;
- v = l->getter(l, engine, callData->thisObject);
- Object *o = v.objectValue();
- if (Q_LIKELY(o)) {
- Scope scope(engine);
- o->construct(scope, callData);
- return scope.result.asReturnedValue();
- }
- return engine->throwTypeError();
+ return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc);
}
-
void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
{
if (!value.isEmpty())
@@ -1267,96 +1137,37 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &
QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- ScopedValue prop(scope, engine->currentContext->getProperty(name));
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name));
// typeof doesn't throw. clear any possible exception
scope.engine->hasException = false;
return method_typeofValue(engine, prop);
}
-#ifndef V4_BOOTSTRAP
-ReturnedValue Runtime::method_typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
-{
- Scope scope(engine);
- ScopedValue prop(scope, method_getQmlScopeObjectProperty(engine, context, propertyIndex, /*captureRequired*/true));
- if (scope.engine->hasException)
- return Encode::undefined();
- return method_typeofValue(engine, prop);
-}
-
-ReturnedValue Runtime::method_typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
-{
- Scope scope(engine);
- ScopedValue prop(scope, method_getQmlContextObjectProperty(engine, context, propertyIndex, /*captureRequired*/true));
- if (scope.engine->hasException)
- return Encode::undefined();
- return method_typeofValue(engine, prop);
-}
-#endif // V4_BOOTSTRAP
-
-QV4::ReturnedValue Runtime::method_typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- ScopedObject obj(scope, base.toObject(engine));
- if (scope.engine->hasException)
- return Encode::undefined();
- ScopedValue prop(scope, obj->get(name));
- return method_typeofValue(engine, prop);
-}
-
-QV4::ReturnedValue Runtime::method_typeofElement(ExecutionEngine *engine, const Value &base, const Value &index)
-{
- Scope scope(engine);
- ScopedString name(scope, index.toString(engine));
- ScopedObject obj(scope, base.toObject(engine));
- if (scope.engine->hasException)
- return Encode::undefined();
- ScopedValue prop(scope, obj->get(name));
- return method_typeofValue(engine, prop);
-}
-
-ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine)
-{
- if (!engine->hasException)
- return Primitive::emptyValue().asReturnedValue();
- return engine->catchException(0);
-}
-
/* The next three methods are a bit tricky. They can't open up a Scope, as that
* would mess up the pushing of the context.
*
* Instead the push/pop pair acts as a non local scope.
*/
-void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine)
-{
- QV4::Value *v = engine->jsAlloca(1);
- Heap::Object *withObject = o.toObject(engine);
- *v = withObject;
- engine->pushContext(engine->currentContext->newWithContext(withObject));
- Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
-}
-
-void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex)
+ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o)
{
- engine->jsAlloca(1); // keep this symmetric with pushWithScope
- ExecutionContext *c = engine->currentContext;
- engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0)));
- Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
+ Q_ASSERT(o.isObject());
+ const Object &obj = static_cast<const Object &>(o);
+ return parent->newWithContext(obj.d())->asReturnedValue();
}
-void Runtime::method_popScope(NoThrowEngine *engine)
+ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex)
{
- Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
- engine->popContext();
- engine->jsStackTop -= 3;
+ ExecutionEngine *e = parent->engine();
+ return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex],
+ e->catchException(0))->asReturnedValue();
}
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- engine->currentContext->createMutableBinding(name, deletable);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable);
}
ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length)
@@ -1367,7 +1178,7 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value
ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
{
Scope scope(engine);
- QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeClasses[classId];
+ QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId];
ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype()));
{
@@ -1406,134 +1217,46 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::
return o.asReturnedValue();
}
-QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine)
+QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
{
- Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext);
- QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext);
- QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject];
- return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue();
+ Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
+ QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject];
+ return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
}
-#endif // V4_BOOTSTRAP
-
-QV4::ReturnedValue Runtime::method_increment(const Value &value)
+QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
{
- TRACE1(value);
-
- if (value.isInteger() && value.integerValue() < INT_MAX)
- return Encode(value.integerValue() + 1);
- else {
- double d = value.toNumber();
- return Encode(d + 1.);
- }
-}
-
-QV4::ReturnedValue Runtime::method_decrement(const Value &value)
-{
- TRACE1(value);
-
- if (value.isInteger() && value.integerValue() > INT_MIN)
- return Encode(value.integerValue() - 1);
- else {
- double d = value.toNumber();
- return Encode(d - 1.);
- }
-}
-
-ReturnedValue Runtime::method_toDouble(const Value &value)
-{
- TRACE1(value);
- return Encode(value.toNumber());
-}
-
-int Runtime::method_toInt(const Value &value)
-{
- TRACE1(value);
- return value.toInt32();
-}
-
-int Runtime::method_doubleToInt(const double &d)
-{
- TRACE0();
- return Primitive::toInt32(d);
-}
-
-unsigned Runtime::method_toUInt(const Value &value)
-{
- TRACE1(value);
- return value.toUInt32();
-}
-
-unsigned Runtime::method_doubleToUInt(const double &d)
-{
- TRACE0();
- return Primitive::toUInt32(d);
+ QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject];
+ return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
}
-#ifndef V4_BOOTSTRAP
-
-ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine)
+ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
return engine->qmlContext()->asReturnedValue();
}
ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
{
- Heap::RegExpObject *ro = engine->newRegExpObject(
- static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)
- ->runtimeRegularExpressions[id].as<RegExp>());
+ Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
return ro->asReturnedValue();
}
-ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
-{
- Scope scope(engine);
- QV4::Scoped<QObjectWrapper> wrapper(scope, object);
- if (!wrapper) {
- engine->throwTypeError(QStringLiteral("Cannot read property of null"));
- return Encode::undefined();
- }
- return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired);
-}
-
-QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)
-{
- QObject *scopeObject = engine->qmlScopeObject();
- QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject);
-
- QJSEngine *jsEngine = engine->jsEngine();
- QQmlData::ensurePropertyCache(jsEngine, attachedObject);
- return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true);
-}
-
-ReturnedValue Runtime::method_getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
+ReturnedValue Runtime::method_loadQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, captureRequired);
+ return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired);
}
-ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
+ReturnedValue Runtime::method_loadQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
-{
- Scope scope(engine);
- QV4::Scoped<QQmlTypeWrapper> wrapper(scope, object);
- if (!wrapper) {
- scope.engine->throwTypeError(QStringLiteral("Cannot read property of null"));
- return Encode::undefined();
- }
- return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired);
+ return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired);
}
-ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index)
+ReturnedValue Runtime::method_loadQmlIdObject(ExecutionEngine *engine, const Value &c, uint index)
{
- Scope scope(engine);
const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
- QQmlContextData *context = *qmlContext.d()->qml->context;
+ QQmlContextData *context = *qmlContext.d()->qml()->context;
if (!context || index >= (uint)context->idValueCount)
return Encode::undefined();
@@ -1544,30 +1267,19 @@ ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Valu
return QObjectWrapper::wrap(engine, context->idValues[index].data());
}
-void Runtime::method_setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
+void Runtime::method_storeQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value);
+ return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value);
}
-void Runtime::method_setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
+void Runtime::method_storeQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, value);
+ return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value);
}
-void Runtime::method_setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)
-{
- Scope scope(engine);
- QV4::Scoped<QObjectWrapper> wrapper(scope, object);
- if (!wrapper) {
- engine->throwTypeError(QStringLiteral("Cannot write property of null"));
- return;
- }
- wrapper->setProperty(engine, propertyIndex, value);
-}
-
-ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine)
+ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine)
{
QQmlContextData *context = engine->callingQmlContext();
if (!context)
@@ -1575,44 +1287,20 @@ ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine)
return context->importedScripts.value();
}
-QV4::ReturnedValue Runtime::method_getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex)
+QV4::ReturnedValue Runtime::method_loadQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex)
{
Scope scope(engine);
- ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
return engine->qmlSingletonWrapper(name);
}
-void Runtime::method_convertThisToObject(ExecutionEngine *engine)
-{
- Value *t = &engine->current->callData->thisObject;
- if (t->isObject())
- return;
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- *t = t->toObject(engine)->asReturnedValue();
- }
-}
-
-ReturnedValue Runtime::method_uPlus(const Value &value)
-{
- TRACE1(value);
-
- if (value.isNumber())
- return value.asReturnedValue();
- if (value.integerCompatible())
- return Encode(value.int_32());
-
- double n = value.toNumberImpl();
- return Encode(n);
-}
-
ReturnedValue Runtime::method_uMinus(const Value &value)
{
TRACE1(value);
// +0 != -0, so we need to convert to double when negating 0
- if (value.isInteger() && value.integerValue())
+ if (value.isInteger() && value.integerValue() &&
+ value.integerValue() != std::numeric_limits<int>::min())
return Encode(-value.integerValue());
else {
double n = RuntimeHelpers::toNumber(value);
@@ -1620,56 +1308,14 @@ ReturnedValue Runtime::method_uMinus(const Value &value)
}
}
-ReturnedValue Runtime::method_complement(const Value &value)
-{
- TRACE1(value);
-
- int n = value.toInt32();
- return Encode((int)~n);
-}
-
-ReturnedValue Runtime::method_uNot(const Value &value)
-{
- TRACE1(value);
-
- bool b = value.toBoolean();
- return Encode(!b);
-}
-
// binary operators
-ReturnedValue Runtime::method_bitOr(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval | rval);
-}
-
-ReturnedValue Runtime::method_bitXor(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval ^ rval);
-}
-
-ReturnedValue Runtime::method_bitAnd(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval & rval);
-}
#ifndef V4_BOOTSTRAP
ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
return add_int32(left.integerValue(), right.integerValue());
if (left.isNumber() && right.isNumber())
return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue();
@@ -1682,7 +1328,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
return sub_int32(left.integerValue(), right.integerValue());
double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
@@ -1695,7 +1341,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
return mul_int32(left.integerValue(), right.integerValue());
double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
@@ -1878,11 +1524,6 @@ Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right
return ! RuntimeHelpers::strictEqual(left, right);
}
-Bool Runtime::method_toBoolean(const Value &value)
-{
- return value.toBoolean();
-}
-
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 0d787714cf..3a26c23990 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -83,8 +83,8 @@ private:
};
# define TRACE0() RuntimeCounters::instance->count(Q_FUNC_INFO);
-# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type());
-# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type(), y->type());
+# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type());
+# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type(), y.type());
#else
# define TRACE0()
# define TRACE1(x)
@@ -99,14 +99,14 @@ enum TypeHint {
struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
- static ReturnedValue toPrimitive(const Value &value, int typeHint);
+ static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint);
static double stringToNumber(const QString &s);
static Heap::String *stringFromNumber(ExecutionEngine *engine, double number);
static double toNumber(const Value &value);
static void numberToString(QString *result, double num, int radix = 10);
- static Heap::String *convertToString(ExecutionEngine *engine, const Value &value);
+ static Heap::String *convertToString(ExecutionEngine *engine, Value value, TypeHint = STRING_HINT);
static Heap::Object *convertToObject(ExecutionEngine *engine, const Value &value);
static Bool equalHelper(const Value &x, const Value &y);
@@ -118,12 +118,11 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
// type conversion and testing
#ifndef V4_BOOTSTRAP
-inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, int typeHint)
+inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint)
{
- const Object *o = value.as<Object>();
- if (!o)
+ if (!value.isObject())
return value.asReturnedValue();
- return RuntimeHelpers::objectDefaultValue(o, typeHint);
+ return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint);
}
#endif
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 302facba06..ea31dfd08b 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -58,7 +58,6 @@ namespace QV4 {
typedef uint Bool;
struct NoThrowEngine;
-
namespace {
template <typename T>
struct ExceptionCheck {
@@ -93,84 +92,64 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
#define FOR_EACH_RUNTIME_METHOD(F) \
/* call */ \
- F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \
- F(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \
- F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \
- F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \
- F(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \
- F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \
- F(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)) \
- F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \
+ F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \
+ F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \
+ F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \
+ F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \
+ F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \
+ F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
+ F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \
\
/* construct */ \
- F(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \
- F(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \
- F(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \
- F(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \
- F(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \
+ F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
\
- /* set & get */ \
- F(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
- F(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
- F(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
- F(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
- F(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)) \
- F(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
+ /* load & store */ \
+ F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
+ F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
+ F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
+ F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
+ F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
+ F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \
+ F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
\
/* typeof */ \
F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \
F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \
- F(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \
- F(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \
- F(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
- F(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
\
/* delete */ \
- F(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
- F(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
- F(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \
- F(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)) \
+ F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
+ F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
+ F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \
+ F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \
\
/* exceptions & scopes */ \
F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
- F(ReturnedValue, unwindException, (ExecutionEngine *engine)) \
- F(void, pushWithScope, (const Value &o, NoThrowEngine *engine)) \
- F(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)) \
- F(void, popScope, (NoThrowEngine *engine)) \
+ F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
+ F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
\
/* function header */ \
F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \
- F(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)) \
- F(void, convertThisToObject, (ExecutionEngine *engine)) \
+ F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \
+ F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \
\
/* literals */ \
F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \
F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \
- F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \
\
/* foreach */ \
F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \
F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \
\
/* unary operators */ \
- F(ReturnedValue, uPlus, (const Value &value)) \
F(ReturnedValue, uMinus, (const Value &value)) \
- F(ReturnedValue, uNot, (const Value &value)) \
- F(ReturnedValue, complement, (const Value &value)) \
- F(ReturnedValue, increment, (const Value &value)) \
- F(ReturnedValue, decrement, (const Value &value)) \
\
/* binary operators */ \
F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \
F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \
F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)) \
- F(ReturnedValue, bitOr, (const Value &left, const Value &right)) \
- F(ReturnedValue, bitXor, (const Value &left, const Value &right)) \
- F(ReturnedValue, bitAnd, (const Value &left, const Value &right)) \
F(ReturnedValue, sub, (const Value &left, const Value &right)) \
F(ReturnedValue, mul, (const Value &left, const Value &right)) \
F(ReturnedValue, div, (const Value &left, const Value &right)) \
@@ -200,28 +179,18 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \
F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \
\
- /* conversions */ \
- F(Bool, toBoolean, (const Value &value)) \
- F(ReturnedValue, toDouble, (const Value &value)) \
- F(int, toInt, (const Value &value)) \
- F(int, doubleToInt, (const double &d)) \
- F(unsigned, toUInt, (const Value &value)) \
- F(unsigned, doubleToUInt, (const double &d)) \
+ F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \
\
/* qml */ \
- F(ReturnedValue, getQmlContext, (NoThrowEngine *engine)) \
- F(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)) \
- F(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \
- F(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)) \
- F(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \
- F(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \
- F(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \
- F(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \
- F(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \
- \
- F(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \
- F(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \
- F(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value))
+ F(ReturnedValue, loadQmlContext, (NoThrowEngine *engine)) \
+ F(ReturnedValue, loadQmlImportedScripts, (NoThrowEngine *engine)) \
+ F(ReturnedValue, loadQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \
+ F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \
+ F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \
+ F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \
+ \
+ F(void, storeQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \
+ F(void, storeQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \
struct Q_QML_PRIVATE_EXPORT Runtime {
Runtime();
diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp
new file mode 100644
index 0000000000..9c115099b5
--- /dev/null
+++ b/src/qml/jsruntime/qv4runtimecodegen.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4runtimecodegen_p.h"
+#include "qv4compilerscanfunctions_p.h"
+
+using namespace QV4;
+
+void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
+ const QString &sourceCode,
+ AST::FunctionExpression *ast,
+ Compiler::Module *module)
+{
+ _module = module;
+ _module->fileName = fileName;
+ _context = 0;
+
+ Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode);
+ // fake a global environment
+ scan.enterEnvironment(0, Compiler::FunctionCode);
+ scan(ast);
+ scan.leaveEnvironment();
+
+ int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
+ _module->rootContext = _module->functions.at(index);
+}
+
+void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail)
+{
+ if (hasError)
+ return;
+ hasError = true;
+ engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn);
+}
+
+void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail)
+{
+ if (hasError)
+ return;
+ hasError = true;
+ engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn);
+}
+
diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index fb68f80eec..be66dc57ca 100644
--- a/src/qml/jit/qv4unop_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
@@ -36,8 +36,8 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QV4UNOP_P_H
-#define QV4UNOP_P_H
+#ifndef QV4RUNTIMECODEGEN_P_H
+#define QV4RUNTIMECODEGEN_P_H
//
// W A R N I N G
@@ -50,43 +50,33 @@
// We mean it.
//
-#include <qv4jsir_p.h>
-#include <qv4isel_masm_p.h>
+#include <private/qv4codegen_p.h>
QT_BEGIN_NAMESPACE
-#if ENABLE(ASSEMBLER)
-
namespace QV4 {
-namespace JIT {
-template <typename JITAssembler>
-struct Unop {
- Unop(JITAssembler *assembler, IR::AluOp operation)
- : _as(assembler)
- , op(operation)
+class RuntimeCodegen : public Compiler::Codegen
+{
+public:
+ RuntimeCodegen(ExecutionEngine *engine, Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
+ : Codegen(jsUnitGenerator, strict)
+ , engine(engine)
{}
- using RelationalCondition = typename JITAssembler::RelationalCondition;
- using PointerToValue = typename JITAssembler::PointerToValue;
- using RuntimeCall = typename JITAssembler::RuntimeCall;
- using TrustedImm32 = typename JITAssembler::TrustedImm32;
-
- void generate(IR::Expr *source, IR::Expr *target);
-
- void generateUMinus(IR::Expr *source, IR::Expr *target);
- void generateNot(IR::Expr *source, IR::Expr *target);
- void generateCompl(IR::Expr *source, IR::Expr *target);
+ void generateFromFunctionExpression(const QString &fileName,
+ const QString &sourceCode,
+ AST::FunctionExpression *ast,
+ Compiler::Module *module);
- JITAssembler *_as;
- IR::AluOp op;
+ void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
+ void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+private:
+ ExecutionEngine *engine;
};
}
-}
-
-#endif
QT_END_NAMESPACE
-#endif
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index 04a0c74133..afb5c21d36 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -52,7 +52,6 @@
#include "qv4engine_p.h"
#include "qv4value_p.h"
-#include "qv4persistent_p.h"
#include "qv4property_p.h"
#ifdef V4_USE_VALGRIND
@@ -71,56 +70,45 @@ struct ScopedValue;
#define CHECK_EXCEPTION() \
do { \
if (scope.hasException()) { \
- scope.result = QV4::Encode::undefined(); \
- return; \
+ return QV4::Encode::undefined(); \
} \
} while (false)
#define RETURN_UNDEFINED() \
- do { \
- scope.result = QV4::Encode::undefined(); \
- return; \
- } while (false)
+ return QV4::Encode::undefined()
#define RETURN_RESULT(r) \
- do { \
- scope.result = r; \
- return; \
- } while (false)
+ return QV4::Encode(r)
#define THROW_TYPE_ERROR() \
- do { \
- scope.result = scope.engine->throwTypeError(); \
- return; \
- } while (false)
+ return scope.engine->throwTypeError()
#define THROW_GENERIC_ERROR(str) \
- do { \
- scope.result = scope.engine->throwError(QString::fromUtf8(str)); \
- return; \
- } while (false)
+ return scope.engine->throwError(QString::fromUtf8(str))
struct Scope {
- inline Scope(ExecutionContext *ctx)
+ explicit Scope(ExecutionContext *ctx)
: engine(ctx->engine())
, mark(engine->jsStackTop)
- , result(*engine->jsAlloca(1))
{
- result = Encode::undefined();
}
explicit Scope(ExecutionEngine *e)
: engine(e)
, mark(engine->jsStackTop)
- , result(*engine->jsAlloca(1))
{
- result = Encode::undefined();
+ }
+
+ explicit Scope(const Managed *m)
+ : engine(m->engine())
+ , mark(engine->jsStackTop)
+ {
}
~Scope() {
#ifndef QT_NO_DEBUG
Q_ASSERT(engine->jsStackTop >= mark);
- Q_ASSERT(engine->currentContext < mark);
+// Q_ASSERT(engine->currentContext < mark);
memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value));
#endif
#ifdef V4_USE_VALGRIND
@@ -139,7 +127,6 @@ struct Scope {
ExecutionEngine *engine;
Value *mark;
- Value &result;
private:
Q_DISABLE_COPY(Scope)
@@ -363,27 +350,6 @@ struct Scoped
Value *ptr;
};
-struct ScopedCallData {
- ScopedCallData(const Scope &scope, int argc = 0)
- {
- int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + qMax(argc , int(QV4::Global::ReservedArgumentCount));
- ptr = reinterpret_cast<CallData *>(scope.alloc(size));
- ptr->tag = quint32(QV4::Value::ValueTypeInternal::Integer);
- ptr->argc = argc;
- }
-
- CallData *operator->() {
- return ptr;
- }
-
- operator CallData *() const {
- return ptr;
- }
-
- CallData *ptr;
-};
-
-
inline Value &Value::operator =(const ScopedValue &v)
{
_val = v.ptr->rawValue();
@@ -411,25 +377,6 @@ struct ScopedProperty
Property *property;
};
-struct ExecutionContextSaver
-{
- Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore.
- ExecutionContext *savedContext;
-
- ExecutionContextSaver(const Scope &scope)
- : scope(scope.engine)
- {
- savedContext = scope.engine->currentContext;
- }
- ~ExecutionContextSaver()
- {
- Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext);
- scope.engine->currentContext = savedContext;
- scope.engine->current = savedContext->d();
- }
-};
-
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 62145f36cc..901f2574da 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -45,6 +45,7 @@
#include "qv4debugging_p.h"
#include "qv4profiling_p.h"
#include "qv4scopedvalue_p.h"
+#include "qv4jscall_p.h"
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
@@ -52,8 +53,7 @@
#include <private/qqmljsast_p.h>
#include <private/qqmlengine_p.h>
#include <private/qv4profiling_p.h>
-#include <qv4jsir_p.h>
-#include <qv4codegen_p.h>
+#include <qv4runtimecodegen_p.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
@@ -61,7 +61,7 @@
using namespace QV4;
Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit)
- : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
+ : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true)
{
if (qml)
@@ -81,16 +81,16 @@ void Script::parse()
if (parsed)
return;
- using namespace QQmlJS;
+ using namespace QV4::Compiler;
parsed = true;
- ExecutionEngine *v4 = scope->engine();
+ ExecutionEngine *v4 = context->engine();
Scope valueScope(v4);
- IR::Module module(v4->debugger() != 0);
+ Module module(v4->debugger() != 0);
- QQmlJS::Engine ee, *engine = &ee;
+ Engine ee, *engine = &ee;
Lexer lexer(engine);
lexer.setCode(sourceCode, line, parseAsBinding);
Parser parser(engine);
@@ -98,7 +98,7 @@ void Script::parse()
const bool parsed = parser.parseProgram();
const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ for (const DiagnosticMessage &m : diagnosticMessages) {
if (m.isError()) {
valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn);
return;
@@ -117,25 +117,15 @@ void Script::parse()
return;
}
- QStringList inheritedLocals;
- if (inheritContext) {
- Scoped<CallContext> ctx(valueScope, scope);
- if (ctx) {
- for (Identifier * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i)
- inheritedLocals.append(*i ? (*i)->string : QString());
- }
- }
-
- RuntimeCodegen cg(v4, strictMode);
- cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals);
+ QV4::Compiler::JSUnitGenerator jsGenerator(&module);
+ RuntimeCodegen cg(v4, &jsGenerator, strictMode);
+ if (inheritContext)
+ cg.setUseFastLookups(false);
+ cg.generateFromProgram(sourceFile, sourceCode, program, &module, compilationMode);
if (v4->hasException)
return;
- QV4::Compiler::JSUnitGenerator jsGenerator(&module);
- QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator));
- if (inheritContext)
- isel->setUseFastLookups(false);
- compilationUnit = isel->compile();
+ compilationUnit = cg.generateCompilationUnit();
vmFunction = compilationUnit->linkToEngine(v4);
}
@@ -153,29 +143,16 @@ ReturnedValue Script::run()
if (!vmFunction)
return Encode::undefined();
- QV4::ExecutionEngine *engine = scope->engine();
+ QV4::ExecutionEngine *engine = context->engine();
QV4::Scope valueScope(engine);
if (qmlContext.isUndefined()) {
TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
- ExecutionContextSaver ctxSaver(valueScope);
- ContextStateSaver stateSaver(valueScope, scope);
- scope->d()->strictMode = vmFunction->isStrict();
- scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups;
- scope->d()->constantTable = vmFunction->compilationUnit->constants;
- scope->d()->compilationUnit = vmFunction->compilationUnit;
-
- return Q_V4_PROFILE(engine, vmFunction);
+ return vmFunction->call(engine->globalObject, 0, 0, context);
} else {
Scoped<QmlContext> qml(valueScope, qmlContext.value());
- ScopedCallData callData(valueScope);
- callData->thisObject = Primitive::undefinedValue();
- if (vmFunction->canUseSimpleFunction())
- qml->simpleCall(valueScope, callData, vmFunction);
- else
- qml->call(valueScope, callData, vmFunction);
- return valueScope.result.asReturnedValue();
+ return vmFunction->call(0, 0, 0, qml);
}
}
@@ -186,24 +163,26 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector)
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+ const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors,
+ Directives *directivesCollector)
{
- using namespace QQmlJS;
+ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
- QQmlJS::Engine ee;
+ Engine ee;
if (directivesCollector)
ee.setDirectives(directivesCollector);
- QQmlJS::Lexer lexer(&ee);
+ Lexer lexer(&ee);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
- QQmlJS::Parser parser(&ee);
+ Parser parser(&ee);
parser.parseProgram();
QList<QQmlError> errors;
const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ for (const DiagnosticMessage &m : diagnosticMessages) {
if (m.isWarning()) {
qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message));
continue;
@@ -230,8 +209,9 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module
return 0;
}
- QQmlJS::Codegen cg(/*strict mode*/false);
- cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::Codegen::EvalCode);
+ Codegen cg(unitGenerator, /*strict mode*/false);
+ cg.setUseFastLookups(false);
+ cg.generateFromProgram(url.toString(), source, program, module, GlobalCode);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
if (reportedErrors)
@@ -239,9 +219,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module
return 0;
}
- QScopedPointer<EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, module, unitGenerator));
- isel->setUseFastLookups(false);
- return isel->compile(/*generate unit data*/false);
+ return cg.generateCompilationUnit(/*generate unit data*/false);
}
QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index 4ebe2dd609..158d21c69d 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -54,6 +54,7 @@
#include "qv4engine_p.h"
#include "qv4functionobject_p.h"
#include "qv4qmlcontext_p.h"
+#include "private/qv4compilercontext_p.h"
#include <QQmlError>
@@ -61,60 +62,16 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
-namespace QQmlJS {
-class Directives;
-}
-
namespace QV4 {
-struct ContextStateSaver {
- Value *savedContext;
- bool strictMode;
- Lookup *lookups;
- const QV4::Value *constantTable;
- CompiledData::CompilationUnitBase *compilationUnit;
- int lineNumber;
-
- ContextStateSaver(const Scope &scope, ExecutionContext *context)
- : savedContext(scope.alloc(1))
- , strictMode(context->d()->strictMode)
- , lookups(context->d()->lookups)
- , constantTable(context->d()->constantTable)
- , compilationUnit(context->d()->compilationUnit)
- , lineNumber(context->d()->lineNumber)
- {
- savedContext->setM(context->d());
- }
- ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context)
- : savedContext(scope.alloc(1))
- , strictMode(context->strictMode)
- , lookups(context->lookups)
- , constantTable(context->constantTable)
- , compilationUnit(context->compilationUnit)
- , lineNumber(context->lineNumber)
- {
- savedContext->setM(context);
- }
-
- ~ContextStateSaver()
- {
- Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m());
- ctx->strictMode = strictMode;
- ctx->lookups = lookups;
- ctx->constantTable = constantTable;
- ctx->compilationUnit = compilationUnit;
- ctx->lineNumber = lineNumber;
- }
-};
-
struct Q_QML_EXPORT Script {
- Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
+ Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
- , scope(scope), strictMode(false), inheritContext(false), parsed(false)
+ , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode)
, vmFunction(0), parseAsBinding(false) {}
Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
- , scope(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false)
+ , context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, vmFunction(0), parseAsBinding(true) {
if (qml)
qmlContext.set(engine, *qml);
@@ -125,10 +82,11 @@ struct Q_QML_EXPORT Script {
int line;
int column;
QString sourceCode;
- ExecutionContext *scope;
+ ExecutionContext *context;
bool strictMode;
bool inheritContext;
bool parsed;
+ QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode;
QV4::PersistentValue qmlContext;
QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
Function *vmFunction;
@@ -139,7 +97,7 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source,
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source,
QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 8afc672aa2..78cd7529d8 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -45,6 +45,7 @@
#include <private/qv4arrayobject_p.h>
#include <private/qqmlengine_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include "qv4runtime_p.h"
#include "qv4objectiterator_p.h"
#include <private/qqmlvaluetypewrapper_p.h>
@@ -66,10 +67,10 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description
QQmlError retn;
retn.setDescription(description);
- QV4::StackFrame frame = v4->currentStackFrame();
+ QV4::CppStackFrame *stackFrame = v4->currentStackFrame;
- retn.setLine(frame.line);
- retn.setUrl(QUrl(frame.source));
+ retn.setLine(stackFrame->lineNumber());
+ retn.setUrl(QUrl(stackFrame->source()));
QQmlEnginePrivate::warning(engine, retn);
}
@@ -416,13 +417,15 @@ public:
bool operator()(typename Container::value_type lhs, typename Container::value_type rhs)
{
QV4::Scope scope(m_v4);
- ScopedObject compare(scope, m_compareFn);
- ScopedCallData callData(scope, 2);
- callData->args[0] = convertElementToValue(m_v4, lhs);
- callData->args[1] = convertElementToValue(m_v4, rhs);
- callData->thisObject = m_v4->globalObject;
- compare->call(scope, callData);
- return scope.result.toNumber() < 0;
+ ScopedFunctionObject compare(scope, m_compareFn);
+ if (!compare)
+ return m_v4->throwTypeError();
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = convertElementToValue(m_v4, lhs);
+ jsCallData->args[1] = convertElementToValue(m_v4, rhs);
+ *jsCallData->thisObject = m_v4->globalObject;
+ QV4::ScopedValue result(scope, compare->call(jsCallData));
+ return result->toNumber() < 0;
}
private:
@@ -438,7 +441,7 @@ public:
loadReference();
}
- if (callData->argc == 1 && callData->args[0].as<FunctionObject>()) {
+ if (callData->argc() == 1 && callData->args[0].as<FunctionObject>()) {
CompareFunctor cf(scope.engine, callData->args[0]);
std::sort(d()->container->begin(), d()->container->end(), cf);
} else {
@@ -450,8 +453,9 @@ public:
storeReference();
}
- static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData)
+ static QV4::ReturnedValue method_get_length(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >());
if (!This)
THROW_TYPE_ERROR();
@@ -464,8 +468,9 @@ public:
RETURN_RESULT(Encode(qint32(This->d()->container->size())));
}
- static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData)
+ static QV4::ReturnedValue method_set_length(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >());
if (!This)
THROW_TYPE_ERROR();
@@ -647,14 +652,20 @@ void SequencePrototype::init()
}
#undef REGISTER_QML_SEQUENCE_METATYPE
-void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue SequencePrototype::method_valueOf(const BuiltinFunction *f, CallData *callData)
{
+ return Encode(callData->thisObject.toString(f->engine()));
+}
+
+ReturnedValue SequencePrototype::method_sort(const BuiltinFunction *b, CallData *callData)
+{
+ Scope scope(b);
QV4::ScopedObject o(scope, callData->thisObject);
if (!o || !o->isListType())
THROW_TYPE_ERROR();
- if (callData->argc >= 2)
- RETURN_RESULT(o);
+ if (callData->argc() >= 2)
+ return o.asReturnedValue();
#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \
@@ -665,7 +676,7 @@ void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, Call
#undef CALL_SORT
{}
- RETURN_RESULT(o);
+ return o.asReturnedValue();
}
#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 2b8d1ea716..169a48c2f9 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -68,12 +68,8 @@ struct SequencePrototype : public QV4::Object
V4_PROTOTYPE(arrayPrototype)
void init();
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
- {
- scope.result = callData->thisObject.toString(scope.engine);
- }
-
- static void method_sort(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_valueOf(const BuiltinFunction *f, CallData *callData);
+ static ReturnedValue method_sort(const BuiltinFunction *, CallData *callData);
static bool isSequenceType(int sequenceTypeId);
static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index c0183a46a7..b38b01c9f7 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -52,17 +52,18 @@ using namespace QV4;
#ifndef V4_BOOTSTRAP
-DEFINE_MANAGED_VTABLE(String);
-
-void String::markObjects(Heap::Base *that, MarkStack *markStack)
+void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
{
- String::Data *s = static_cast<String::Data *>(that);
+ String *s = static_cast<String *>(that);
if (s->largestSubLength) {
s->left->mark(markStack);
s->right->mark(markStack);
}
}
+DEFINE_MANAGED_VTABLE(String);
+
+
bool String::isEqualTo(Managed *t, Managed *o)
{
if (t == o)
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 2f34dd6139..85345aca4d 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -65,6 +65,7 @@ struct Identifier;
namespace Heap {
struct Q_QML_PRIVATE_EXPORT String : Base {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
enum StringType {
StringType_Unknown,
StringType_Regular,
@@ -181,7 +182,8 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
makeIdentifierImpl();
}
- void makeIdentifierImpl() const;
+ // slow path
+ Q_NEVER_INLINE void makeIdentifierImpl() const;
static uint createHashValue(const QChar *ch, int length, uint *subtype)
{
@@ -205,7 +207,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
Identifier *identifier() const { return d()->identifier; }
protected:
- static void markObjects(Heap::Base *that, MarkStack *markStack);
static bool isEqualTo(Managed *that, Managed *o);
static uint getLength(const Managed *m);
#endif
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 7c65c97d73..efab51ec53 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -45,17 +45,11 @@
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
#include "qv4alloca_p.h"
+#include "qv4jscall_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
-#include <qv4jsir_p.h>
-#include <qv4codegen_p.h>
-
#include <cassert>
#ifndef Q_OS_WIN
@@ -108,11 +102,8 @@ bool StringObject::deleteIndexedProperty(Managed *m, uint index)
Scoped<StringObject> o(scope, m->as<StringObject>());
Q_ASSERT(!!o);
- if (index < static_cast<uint>(o->d()->string->toQString().length())) {
- if (v4->current->strictMode)
- v4->throwTypeError();
+ if (index < static_cast<uint>(o->d()->string->toQString().length()))
return false;
- }
return true;
}
@@ -152,24 +143,25 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("String"));
}
-void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
+ ExecutionEngine *v4 = static_cast<const Object *>(f)->engine();
+ Scope scope(v4);
ScopedString value(scope);
- if (callData->argc)
- value = callData->args[0].toString(v4);
+ if (argc)
+ value = argv[0].toString(v4);
else
value = v4->newString();
- scope.result = Encode(v4->newStringObject(value));
+ return Encode(v4->newStringObject(value));
}
-void StringCtor::call(const Managed *, Scope &scope, CallData *callData)
+ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
- ExecutionEngine *v4 = scope.engine;
- if (callData->argc)
- scope.result = callData->args[0].toString(v4);
+ ExecutionEngine *v4 = m->engine();
+ if (argc)
+ return argv[0].toString(v4)->asReturnedValue();
else
- scope.result = v4->newString();
+ return v4->newString()->asReturnedValue();
}
void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -208,140 +200,154 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("trim"), method_trim);
}
-static QString getThisString(Scope &scope, CallData *callData)
+static QString getThisString(ExecutionEngine *v4, const Value *thisObject)
{
- ScopedValue t(scope, callData->thisObject);
- if (String *s = t->stringValue())
+ if (String *s = thisObject->stringValue())
return s->toQString();
- if (StringObject *thisString = t->as<StringObject>())
+ if (const StringObject *thisString = thisObject->as<StringObject>())
return thisString->d()->string->toQString();
- if (t->isUndefined() || t->isNull()) {
- scope.engine->throwTypeError();
+ if (thisObject->isUndefined() || thisObject->isNull()) {
+ v4->throwTypeError();
return QString();
}
- return t->toQString();
+ return thisObject->toQString();
}
-void StringPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- if (callData->thisObject.isString())
- RETURN_RESULT(callData->thisObject);
+ if (thisObject->isString())
+ return thisObject->asReturnedValue();
- StringObject *o = callData->thisObject.as<StringObject>();
+ ExecutionEngine *v4 = b->engine();
+ const StringObject *o = thisObject->as<StringObject>();
if (!o)
- THROW_TYPE_ERROR();
- scope.result = o->d()->string;
+ return v4->throwTypeError();
+ return o->d()->string->asReturnedValue();
}
-void StringPrototype::method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_charAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString str = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString str = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
int pos = 0;
- if (callData->argc > 0)
- pos = (int) callData->args[0].toInteger();
+ if (argc > 0)
+ pos = (int) argv[0].toInteger();
QString result;
if (pos >= 0 && pos < str.length())
result += str.at(pos);
- scope.result = scope.engine->newString(result);
+ return Encode(v4->newString(result));
}
-void StringPrototype::method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString str = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString str = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
int pos = 0;
- if (callData->argc > 0)
- pos = (int) callData->args[0].toInteger();
+ if (argc > 0)
+ pos = (int) argv[0].toInteger();
if (pos >= 0 && pos < str.length())
RETURN_RESULT(Encode(str.at(pos).unicode()));
- scope.result = Encode(qt_qnan());
+ return Encode(qt_qnan());
}
-void StringPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+ Scope scope(v4);
ScopedString s(scope);
- for (int i = 0; i < callData->argc; ++i) {
- s = callData->args[i].toString(scope.engine);
- CHECK_EXCEPTION();
+ for (int i = 0; i < argc; ++i) {
+ s = argv[i].toString(scope.engine);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
Q_ASSERT(s->isString());
value += s->toQString();
}
- scope.result = scope.engine->newString(value);
+ return Encode(v4->newString(value));
}
-void StringPrototype::method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
QString searchString;
- if (callData->argc) {
- if (callData->args[0].as<RegExpObject>())
- THROW_TYPE_ERROR();
- searchString = callData->args[0].toQString();
+ if (argc) {
+ if (argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ searchString = argv[0].toQString();
}
int pos = value.length();
- if (callData->argc > 1)
- pos = (int) callData->args[1].toInteger();
+ if (argc > 1)
+ pos = (int) argv[1].toInteger();
if (pos == value.length())
RETURN_RESULT(Encode(value.endsWith(searchString)));
QStringRef stringToSearch = value.leftRef(pos);
- scope.result = Encode(stringToSearch.endsWith(searchString));
+ return Encode(stringToSearch.endsWith(searchString));
}
-void StringPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
QString searchString;
- if (callData->argc)
- searchString = callData->args[0].toQString();
+ if (argc)
+ searchString = argv[0].toQString();
int pos = 0;
- if (callData->argc > 1)
- pos = (int) callData->args[1].toInteger();
+ if (argc > 1)
+ pos = (int) argv[1].toInteger();
int index = -1;
if (! value.isEmpty())
index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
- scope.result = Encode(index);
+ return Encode(index);
}
-void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
QString searchString;
- if (callData->argc) {
- if (callData->args[0].as<RegExpObject>())
- THROW_TYPE_ERROR();
- searchString = callData->args[0].toQString();
+ if (argc) {
+ if (argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ searchString = argv[0].toQString();
}
int pos = 0;
- if (callData->argc > 1) {
- ScopedValue posArg(scope, callData->argument(1));
- pos = (int) posArg->toInteger();
- if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber()))
+ if (argc > 1) {
+ const Value &posArg = argv[1];
+ pos = (int) posArg.toInteger();
+ if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber()))
pos = value.length();
}
@@ -349,20 +355,21 @@ void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, Cal
RETURN_RESULT(Encode(value.contains(searchString)));
QStringRef stringToSearch = value.midRef(pos);
- scope.result = Encode(stringToSearch.contains(searchString));
+ return Encode(stringToSearch.contains(searchString));
}
-void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
QString searchString;
- if (callData->argc)
- searchString = callData->args[0].toQString();
+ if (argc)
+ searchString = argv[0].toQString();
- ScopedValue posArg(scope, callData->argument(1));
- double position = RuntimeHelpers::toNumber(posArg);
+ double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf();
if (std::isnan(position))
position = +qInf();
else
@@ -374,97 +381,84 @@ void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope,
if (searchString.isNull() && pos == 0)
RETURN_RESULT(Encode(-1));
int index = value.lastIndexOf(searchString, pos);
- scope.result = Encode(index);
+ return Encode(index);
}
-void StringPrototype::method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_localeCompare(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- ScopedValue v(scope, callData->argument(0));
- const QString that = v->toQString();
- scope.result = Encode(QString::localeAwareCompare(value, that));
+ const QString that = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ return Encode(QString::localeAwareCompare(value, that));
}
-void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- if (callData->thisObject.isUndefined() || callData->thisObject.isNull())
- THROW_TYPE_ERROR();
+ ExecutionEngine *v4 = b->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
- ScopedString s(scope, callData->thisObject.toString(scope.engine));
-
- ScopedValue regexp(scope, callData->argument(0));
- Scoped<RegExpObject> rx(scope, regexp);
- if (!rx) {
- ScopedCallData callData(scope, 1);
- callData->args[0] = regexp;
- scope.engine->regExpCtor()->construct(scope, callData);
- rx = scope.result.asReturnedValue();
+ Scope scope(v4);
+ ScopedString s(scope, thisObject->toString(v4));
+ if (v4->hasException)
+ return Encode::undefined();
+
+ Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue());
+ if (!that) {
+ // convert args[0] to a regexp
+ that = RegExpCtor::callAsConstructor(b, argv, argc);
+ if (v4->hasException)
+ return Encode::undefined();
}
+ Q_ASSERT(!!that);
- if (!rx)
- // ### CHECK
- THROW_TYPE_ERROR();
-
- bool global = rx->global();
+ bool global = that->global();
- // ### use the standard builtin function, not the one that might be redefined in the proto
- ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec")));
- ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString));
+ if (!global)
+ return RegExpPrototype::method_exec(b, that, s, 1);
- ScopedCallData cData(scope, 1);
- cData->thisObject = rx;
- cData->args[0] = s;
- if (!global) {
- exec->call(scope, cData);
- return;
- }
-
- ScopedString lastIndex(scope, scope.engine->newString(QStringLiteral("lastIndex")));
- rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0)));
+ // rx is now in thisObject
+ that->setLastIndex(0);
ScopedArrayObject a(scope, scope.engine->newArrayObject());
- double previousLastIndex = 0;
+ int previousLastIndex = 0;
uint n = 0;
- ScopedValue matchStr(scope);
- ScopedValue index(scope);
while (1) {
- exec->call(scope, cData);
- if (scope.result.isNull())
+ Value result = Primitive::fromReturnedValue(RegExpPrototype::execFirstMatch(b, that, s, 1));
+ if (result.isNull())
break;
- assert(scope.result.isObject());
- index = rx->get(lastIndex, 0);
- double thisIndex = index->toInteger();
- if (previousLastIndex == thisIndex) {
- previousLastIndex = thisIndex + 1;
- rx->put(lastIndex, ScopedValue(scope, Primitive::fromDouble(previousLastIndex)));
+ int index = that->lastIndex();
+ if (previousLastIndex == index) {
+ previousLastIndex = index + 1;
+ that->setLastIndex(previousLastIndex);
} else {
- previousLastIndex = thisIndex;
+ previousLastIndex = index;
}
- matchStr = scope.result.objectValue()->getIndexed(0);
- a->arraySet(n, matchStr);
+ a->arraySet(n, result);
++n;
}
if (!n)
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = a;
+ return a.asReturnedValue();
}
-void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- double repeats = callData->args[0].toInteger();
+ double repeats = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
- if (repeats < 0 || qIsInf(repeats)) {
- scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value"));
- return;
- }
+ if (repeats < 0 || qIsInf(repeats))
+ return v4->throwRangeError(QLatin1String("Invalid count value"));
- scope.result = scope.engine->newString(value.repeated(int(repeats)));
+ return Encode(v4->newString(value.repeated(int(repeats))));
}
static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
@@ -513,13 +507,13 @@ static void appendReplacementString(QString *result, const QString &input, const
}
}
-void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
QString string;
- if (StringObject *thisString = callData->thisObject.as<StringObject>())
+ if (const StringObject *thisString = thisObject->as<StringObject>())
string = thisString->d()->string->toQString();
else
- string = callData->thisObject.toQString();
+ string = thisObject->toQString();
int numCaptures = 0;
int numStringMatches = 0;
@@ -528,7 +522,8 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call
uint _matchOffsets[64];
uint *matchOffsets = _matchOffsets;
- ScopedValue searchValue(scope, callData->argument(0));
+ Scope scope(b);
+ ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue());
Scoped<RegExpObject> regExp(scope, searchValue);
if (regExp) {
uint offset = 0;
@@ -571,14 +566,14 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call
}
QString result;
- ScopedValue replaceValue(scope, callData->argument(1));
+ ScopedValue replacement(scope);
+ ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
ScopedFunctionObject searchCallback(scope, replaceValue);
if (!!searchCallback) {
result.reserve(string.length() + 10*numStringMatches);
- ScopedCallData callData(scope, numCaptures + 2);
- callData->thisObject = Primitive::undefinedValue();
- int lastEnd = 0;
ScopedValue entry(scope);
+ Value *arguments = scope.alloc(numCaptures + 2);
+ int lastEnd = 0;
for (int i = 0; i < numStringMatches; ++i) {
for (int k = 0; k < numCaptures; ++k) {
int idx = (i * numCaptures + k) * 2;
@@ -587,17 +582,18 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call
entry = Primitive::undefinedValue();
if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
entry = scope.engine->newString(string.mid(start, end - start));
- callData->args[k] = entry;
+ arguments[k] = entry;
}
uint matchStart = matchOffsets[i * numCaptures * 2];
Q_ASSERT(matchStart >= static_cast<uint>(lastEnd));
uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
- callData->args[numCaptures] = Primitive::fromUInt32(matchStart);
- callData->args[numCaptures + 1] = scope.engine->newString(string);
+ arguments[numCaptures] = Primitive::fromUInt32(matchStart);
+ arguments[numCaptures + 1] = scope.engine->newString(string);
- searchCallback->call(scope, callData);
+ Value that = Primitive::undefinedValue();
+ replacement = searchCallback->call(&that, arguments, numCaptures + 2);
result += string.midRef(lastEnd, matchStart - lastEnd);
- result += scope.result.toQString();
+ result += replacement->toQString();
lastEnd = matchEnd;
}
result += string.midRef(lastEnd);
@@ -623,44 +619,45 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call
if (matchOffsets != _matchOffsets)
free(matchOffsets);
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
-void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_search(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString string = getThisString(scope, callData);
- scope.result = callData->argument(0);
- CHECK_EXCEPTION();
+ Scope scope(b);
+ QString string = getThisString(scope.engine, thisObject);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>());
+ Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Primitive::undefinedValue());
if (!regExp) {
- ScopedCallData callData(scope, 1);
- callData->args[0] = scope.result;
- scope.engine->regExpCtor()->construct(scope, callData);
- CHECK_EXCEPTION();
+ regExp = scope.engine->regExpCtor()->callAsConstructor(argv, 1);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
- regExp = scope.result.as<RegExpObject>();
Q_ASSERT(regExp);
}
Scoped<RegExp> re(scope, regExp->value());
Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint));
uint result = re->match(string, /*offset*/0, matchOffsets);
if (result == JSC::Yarr::offsetNoMatch)
- scope.result = Encode(-1);
+ return Encode(-1);
else
- scope.result = Encode(result);
+ return Encode(result);
}
-void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString text = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString text = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
const double length = text.length();
- double start = callData->argc ? callData->args[0].toInteger() : 0;
- double end = (callData->argc < 2 || callData->args[1].isUndefined())
- ? length : callData->args[1].toInteger();
+ double start = argc ? argv[0].toInteger() : 0;
+ double end = (argc < 2 || argv[1].isUndefined())
+ ? length : argv[1].toInteger();
if (start < 0)
start = qMax(length + start, 0.);
@@ -676,16 +673,19 @@ void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDa
const int intEnd = int(end);
int count = qMax(0, intEnd - intStart);
- scope.result = scope.engine->newString(text.mid(intStart, count));
+ return Encode(v4->newString(text.mid(intStart, count)));
}
-void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString text = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ QString text = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- ScopedValue separatorValue(scope, callData->argument(0));
- ScopedValue limitValue(scope, callData->argument(1));
+ Scope scope(v4);
+ ScopedValue separatorValue(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue limitValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
ScopedArrayObject array(scope, scope.engine->newArrayObject());
@@ -693,7 +693,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa
if (limitValue->isUndefined()) {
ScopedString s(scope, scope.engine->newString(text));
array->push_back(s);
- RETURN_RESULT(array);
+ return array.asReturnedValue();
}
RETURN_RESULT(scope.engine->newString(text.left(limitValue->toInteger())));
}
@@ -701,7 +701,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa
uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32();
if (limit == 0)
- RETURN_RESULT(array);
+ return array.asReturnedValue();
Scoped<RegExpObject> re(scope, separatorValue);
if (re) {
@@ -742,7 +742,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa
if (separator.isEmpty()) {
for (uint i = 0; i < qMin(limit, uint(text.length())); ++i)
array->push_back((s = scope.engine->newString(text.mid(i, 1))));
- RETURN_RESULT(array);
+ return array.asReturnedValue();
}
int start = 0;
@@ -756,44 +756,48 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa
if (array->getLength() < limit && start != -1)
array->push_back((s = scope.engine->newString(text.mid(start))));
}
- RETURN_RESULT(array);
+ return array.asReturnedValue();
}
-void StringPrototype::method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
QString searchString;
- if (callData->argc) {
- if (callData->args[0].as<RegExpObject>())
- THROW_TYPE_ERROR();
- searchString = callData->args[0].toQString();
+ if (argc) {
+ if (argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ searchString = argv[0].toQString();
}
int pos = 0;
- if (callData->argc > 1)
- pos = (int) callData->args[1].toInteger();
+ if (argc > 1)
+ pos = (int) argv[1].toInteger();
if (pos == 0)
- RETURN_RESULT(Encode(value.startsWith(searchString)));
+ return Encode(value.startsWith(searchString));
QStringRef stringToSearch = value.midRef(pos);
RETURN_RESULT(Encode(stringToSearch.startsWith(searchString)));
}
-void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- const QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
double start = 0;
- if (callData->argc > 0)
- start = callData->args[0].toInteger();
+ if (argc > 0)
+ start = argv[0].toInteger();
double length = +qInf();
- if (callData->argc > 1)
- length = callData->args[1].toInteger();
+ if (argc > 1)
+ length = argv[1].toInteger();
double count = value.length();
if (start < 0)
@@ -803,25 +807,26 @@ void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallD
qint32 x = Primitive::toInt32(start);
qint32 y = Primitive::toInt32(length);
- scope.result = scope.engine->newString(value.mid(x, y));
+ return Encode(v4->newString(value.mid(x, y)));
}
-void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_substring(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
int length = value.length();
double start = 0;
double end = length;
- if (callData->argc > 0)
- start = callData->args[0].toInteger();
+ if (argc > 0)
+ start = argv[0].toInteger();
- ScopedValue endValue(scope, callData->argument(1));
- if (!endValue->isUndefined())
- end = endValue->toInteger();
+ if (argc > 1 && !argv[1].isUndefined())
+ end = argv[1].toInteger();
if (std::isnan(start) || start < 0)
start = 0;
@@ -843,50 +848,56 @@ void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, Ca
qint32 x = (int)start;
qint32 y = (int)(end - start);
- scope.result = scope.engine->newString(value.mid(x, y));
+ return Encode(v4->newString(value.mid(x, y)));
}
-void StringPrototype::method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_toLowerCase(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- scope.result = scope.engine->newString(value.toLower());
+ return Encode(v4->newString(value.toLower()));
}
-void StringPrototype::method_toLocaleLowerCase(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_toLocaleLowerCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- method_toLowerCase(b, scope, callData);
+ return method_toLowerCase(b, thisObject, argv, argc);
}
-void StringPrototype::method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_toUpperCase(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- QString value = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- scope.result = scope.engine->newString(value.toUpper());
+ return Encode(v4->newString(value.toUpper()));
}
-void StringPrototype::method_toLocaleUpperCase(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- return method_toUpperCase(b, scope, callData);
+ return method_toUpperCase(b, thisObject, argv, argc);
}
-void StringPrototype::method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- QString str(callData->argc, Qt::Uninitialized);
+ QString str(argc, Qt::Uninitialized);
QChar *ch = str.data();
- for (int i = 0; i < callData->argc; ++i) {
- *ch = QChar(callData->args[i].toUInt16());
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ *ch = QChar(argv[i].toUInt16());
++ch;
}
- scope.result = scope.engine->newString(str);
+ return Encode(b->engine()->newString(str));
}
-void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- QString s = getThisString(scope, callData);
- CHECK_EXCEPTION();
+ ExecutionEngine *v4 = b->engine();
+ QString s = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
const QChar *chars = s.constData();
int start, end;
@@ -899,5 +910,5 @@ void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallDat
break;
}
- scope.result = scope.engine->newString(QString(chars + start, end - start + 1));
+ return Encode(v4->newString(QString(chars + start, end - start + 1)));
}
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index ce046f4844..7d25678b61 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -64,7 +64,7 @@ namespace Heap {
Member(class, Pointer, String *, string)
DECLARE_HEAP_OBJECT(StringObject, Object) {
- DECLARE_MARK_TABLE(StringObject);
+ DECLARE_MARKOBJECTS(StringObject);
enum {
LengthPropertyIndex = 0
@@ -106,8 +106,8 @@ struct StringCtor: FunctionObject
{
V4_OBJECT2(StringCtor, FunctionObject)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct StringPrototype: StringObject
@@ -115,30 +115,30 @@ struct StringPrototype: StringObject
V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_concat(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_includes(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_split(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_substr(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_substring(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toLocaleUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_trim(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_split(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_startsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_substr(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_substring(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index fe27d7c2d2..4ba31f9b6e 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -39,6 +39,7 @@
#include "qv4typedarray_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
+#include "qv4jscall_p.h"
#include <cmath>
@@ -208,36 +209,32 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t
type = t;
}
-void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
{
- Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m));
+ Scope scope(f->engine());
+ const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f);
- if (!callData->argc || !callData->args[0].isObject()) {
+ if (!argc || !argv[0].isObject()) {
// ECMA 6 22.2.1.1
- double l = callData->argc ? callData->args[0].toNumber() : 0;
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ double l = argc ? argv[0].toNumber() : 0;
+ if (scope.engine->hasException)
+ return Encode::undefined();
uint len = (uint)l;
if (l != len)
scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
uint byteLength = len * operations[that->d()->type].bytesPerElement;
Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength));
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.engine->hasException)
+ return Encode::undefined();
Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer.set(scope.engine, buffer->d());
array->d()->byteLength = byteLength;
array->d()->byteOffset = 0;
- scope.result = array.asReturnedValue();
- return;
+ return array.asReturnedValue();
}
- Scoped<TypedArray> typedArray(scope, callData->argument(0));
+ Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Primitive::undefinedValue());
if (!!typedArray) {
// ECMA 6 22.2.1.2
Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer);
@@ -247,10 +244,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat
uint destByteLength = byteLength*destElementSize/srcElementSize;
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength));
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.engine->hasException)
+ return Encode::undefined();
Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer.set(scope.engine, newBuffer->d());
@@ -275,39 +270,30 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat
}
}
- scope.result = array.asReturnedValue();
- return;
+ return array.asReturnedValue();
}
- Scoped<ArrayBuffer> buffer(scope, callData->argument(0));
+ Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue());
if (!!buffer) {
// ECMA 6 22.2.1.4
- double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0;
+ double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0;
uint byteOffset = (uint)dbyteOffset;
uint elementSize = operations[that->d()->type].bytesPerElement;
- if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) {
- scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
- return;
- }
+ if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength())
+ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
uint byteLength;
- if (callData->argc < 3 || callData->args[2].isUndefined()) {
+ if (argc < 3 || argv[2].isUndefined()) {
byteLength = buffer->byteLength() - byteOffset;
- if (buffer->byteLength() < byteOffset || byteLength % elementSize) {
- scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
- return;
- }
+ if (buffer->byteLength() < byteOffset || byteLength % elementSize)
+ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
} else {
- double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX);
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX);
+ if (scope.engine->hasException)
+ return Encode::undefined();
l *= elementSize;
- if (buffer->byteLength() - byteOffset < l) {
- scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
- return;
- }
+ if (buffer->byteLength() - byteOffset < l)
+ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
byteLength = (uint)l;
}
@@ -315,25 +301,20 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat
array->d()->buffer.set(scope.engine, buffer->d());
array->d()->byteLength = byteLength;
array->d()->byteOffset = byteOffset;
- scope.result = array.asReturnedValue();
- return;
+ return array.asReturnedValue();
}
// ECMA 6 22.2.1.3
- ScopedObject o(scope, callData->argument(0));
+ ScopedObject o(scope, argc ? argv[0] : Primitive::undefinedValue());
uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX);
- if (scope.engine->hasException) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ if (scope.engine->hasException)
+ return scope.engine->throwTypeError();
uint elementSize = operations[that->d()->type].bytesPerElement;
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize));
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.engine->hasException)
+ return Encode::undefined();
Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer.set(scope.engine, newBuffer->d());
@@ -346,21 +327,19 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat
while (idx < l) {
val = o->getIndexed(idx);
array->d()->type->write(scope.engine, b, 0, val);
- if (scope.engine->hasException) {
- scope.result = Encode::undefined();
- return;
- }
+ if (scope.engine->hasException)
+ return Encode::undefined();
++idx;
b += elementSize;
}
- scope.result = array.asReturnedValue();
+ return array.asReturnedValue();
}
-void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- construct(that, scope, callData);
+ return callAsConstructor(f, argv, argc);
}
void Heap::TypedArray::init(Type t)
@@ -406,15 +385,10 @@ bool TypedArray::putIndexed(Managed *m, uint index, const Value &value)
uint bytesPerElement = a->d()->type->bytesPerElement;
uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength())
- goto reject;
+ return false;
a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value);
return true;
-
-reject:
- if (scope.engine->current->strictMode)
- scope.engine->throwTypeError();
- return false;
}
void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
@@ -435,52 +409,57 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
}
-void TypedArrayPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<TypedArray> v(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = v->d()->buffer;
+ return v->d()->buffer->asReturnedValue();
}
-void TypedArrayPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<TypedArray> v(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = Encode(v->d()->byteLength);
+ return Encode(v->d()->byteLength);
}
-void TypedArrayPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<TypedArray> v(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = Encode(v->d()->byteOffset);
+ return Encode(v->d()->byteOffset);
}
-void TypedArrayPrototype::method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- Scoped<TypedArray> v(scope, callData->thisObject);
+ ExecutionEngine *v4 = b->engine();
+ const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
- THROW_TYPE_ERROR();
+ return v4->throwTypeError();
- scope.result = Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
+ return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
}
-void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<TypedArray> a(scope, callData->thisObject);
+ Scope scope(b);
+ Scoped<TypedArray> a(scope, *thisObject);
if (!a)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
if (!buffer)
scope.engine->throwTypeError();
- double doffset = callData->argc >= 2 ? callData->args[1].toInteger() : 0;
+ double doffset = argc >= 2 ? argv[1].toInteger() : 0;
if (scope.engine->hasException)
RETURN_UNDEFINED();
@@ -489,17 +468,17 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call
uint offset = (uint)doffset;
uint elementSize = a->d()->type->bytesPerElement;
- Scoped<TypedArray> srcTypedArray(scope, callData->args[0]);
+ Scoped<TypedArray> srcTypedArray(scope, argv[0]);
if (!srcTypedArray) {
// src is a regular object
- ScopedObject o(scope, callData->args[0].toObject(scope.engine));
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
if (scope.engine->hasException || !o)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber();
uint l = (uint)len;
if (scope.engine->hasException || l != len)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
if (offset + l > a->length())
RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
@@ -521,7 +500,7 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call
// src is a typed array
Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
if (!srcBuffer)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
uint l = srcTypedArray->length();
if (offset + l > a->length())
@@ -559,24 +538,25 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call
RETURN_UNDEFINED();
}
-void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
- Scoped<TypedArray> a(scope, callData->thisObject);
+ Scope scope(builtin);
+ Scoped<TypedArray> a(scope, *thisObject);
if (!a)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
if (!buffer)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
int len = a->length();
- double b = callData->argc > 0 ? callData->args[0].toInteger() : 0;
+ double b = argc > 0 ? argv[0].toInteger() : 0;
if (b < 0)
b = len + b;
uint begin = (uint)qBound(0., b, (double)len);
- double e = callData->argc < 2 || callData->args[1].isUndefined() ? len : callData->args[1].toInteger();
+ double e = argc < 2 || argv[1].isUndefined() ? len : argv[1].toInteger();
if (e < 0)
e = len + e;
uint end = (uint)qBound(0., e, (double)len);
@@ -590,11 +570,11 @@ void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope,
ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor()));
if (!constructor)
- THROW_TYPE_ERROR();
+ return scope.engine->throwTypeError();
- ScopedCallData cData(scope, 3);
- cData->args[0] = buffer;
- cData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
- cData->args[2] = Encode(newLen);
- constructor->construct(scope, cData);
+ Value *arguments = scope.alloc(3);
+ arguments[0] = buffer;
+ arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
+ arguments[2] = Encode(newLen);
+ return constructor->callAsConstructor(arguments, 3);
}
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index a472dfa607..129c662c97 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -80,7 +80,7 @@ namespace Heap {
Member(class, NoMark, uint, arrayType)
DECLARE_HEAP_OBJECT(TypedArray, Object) {
- DECLARE_MARK_TABLE(TypedArray);
+ DECLARE_MARKOBJECTS(TypedArray);
enum Type {
Int8Array,
UInt8Array,
@@ -141,8 +141,8 @@ struct TypedArrayCtor: FunctionObject
{
V4_OBJECT2(TypedArrayCtor, FunctionObject)
- static void construct(const Managed *m, Scope &scope, CallData *callData);
- static void call(const Managed *that, Scope &scope, CallData *callData);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
@@ -153,13 +153,13 @@ struct TypedArrayPrototype : Object
void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
- static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
inline void
diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp
index f41442df7a..0d4711df3c 100644
--- a/src/qml/jsruntime/qv4value.cpp
+++ b/src/qml/jsruntime/qv4value.cpp
@@ -75,39 +75,29 @@ int Value::toUInt16() const
return (unsigned short)number;
}
-bool Value::toBoolean() const
+bool Value::toBooleanImpl(Value val)
{
- if (isInteger() || isBoolean())
- return static_cast<bool>(int_32());
-
- if (isUndefined() || isNull())
- return false;
-
- if (isManaged()) {
+ if (val.isManagedOrUndefined()) {
+ Heap::Base *b = val.m();
+ if (!b)
+ return false;
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
#else
- if (String *s = stringValue())
- return s->toQString().length() > 0;
+ if (b->vtable()->isString)
+ return static_cast<Heap::String *>(b)->length() > 0;
#endif
return true;
}
// double
- return doubleValue() && !std::isnan(doubleValue());
+ double d = val.doubleValue();
+ return d && !std::isnan(d);
}
-double Value::toInteger() const
+double Value::toNumberImpl(Value val)
{
- if (integerCompatible())
- return int_32();
-
- return Primitive::toInteger(toNumber());
-}
-
-double Value::toNumberImpl() const
-{
- switch (type()) {
+ switch (val.type()) {
case QV4::Value::Undefined_Type:
return std::numeric_limits<double>::quiet_NaN();
case QV4::Value::Managed_Type:
@@ -115,12 +105,13 @@ double Value::toNumberImpl() const
Q_UNIMPLEMENTED();
Q_FALLTHROUGH();
#else
- if (String *s = stringValue())
+ if (String *s = val.stringValue())
return RuntimeHelpers::stringToNumber(s->toQString());
{
- Q_ASSERT(isObject());
- Scope scope(objectValue()->engine());
- ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT));
+ Q_ASSERT(val.isObject());
+ Scope scope(val.objectValue()->engine());
+ ScopedValue protectThis(scope, val);
+ ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT));
if (scope.engine->hasException)
return 0;
return prim->toNumber();
@@ -129,7 +120,7 @@ double Value::toNumberImpl() const
case QV4::Value::Null_Type:
case QV4::Value::Boolean_Type:
case QV4::Value::Integer_Type:
- return int_32();
+ return val.int_32();
default: // double
Q_UNREACHABLE();
}
@@ -236,83 +227,23 @@ bool Value::sameValue(Value other) const {
if (s && os)
return s->isEqualTo(os);
if (isInteger() && other.isDouble())
- return int_32() ? (double(int_32()) == other.doubleValue()) : (other._val == 0);
+ return int_32() ? (double(int_32()) == other.doubleValue())
+ : (other.doubleValue() == 0 && !std::signbit(other.doubleValue()));
if (isDouble() && other.isInteger())
- return other.int_32() ? (doubleValue() == double(other.int_32())) : (_val == 0);
+ return other.int_32() ? (doubleValue() == double(other.int_32()))
+ : (doubleValue() == 0 && !std::signbit(doubleValue()));
return false;
}
-
-int Primitive::toInt32(double number)
-{
- const double D32 = 4294967296.0;
- const double D31 = D32 / 2.0;
-
- if ((number >= -D31 && number < D31))
- return static_cast<int>(number);
-
-
- if (!std::isfinite(number))
- return 0;
-
- double d = ::floor(::fabs(number));
- if (std::signbit(number))
- d = -d;
-
- number = ::fmod(d , D32);
-
- if (number < -D31)
- number += D32;
- else if (number >= D31)
- number -= D32;
-
- return int(number);
-}
-
-unsigned int Primitive::toUInt32(double number)
-{
- const double D32 = 4294967296.0;
- if ((number >= 0 && number < D32))
- return static_cast<uint>(number);
-
- if (!std::isfinite(number))
- return +0;
-
- double d = ::floor(::fabs(number));
- if (std::signbit(number))
- d = -d;
-
- number = ::fmod(d , D32);
-
- if (number < 0)
- number += D32;
-
- return unsigned(number);
-}
-
-double Primitive::toInteger(double number)
-{
- if (std::isnan(number))
- return +0;
- else if (! number || std::isinf(number))
- return number;
- const double v = floor(fabs(number));
- return std::signbit(number) ? -v : v;
-}
-
#ifndef V4_BOOTSTRAP
-Heap::String *Value::toString(ExecutionEngine *e) const
+Heap::String *Value::toString(ExecutionEngine *e, Value val)
{
- if (String *s = stringValue())
- return s->d();
- return RuntimeHelpers::convertToString(e, *this);
+ return RuntimeHelpers::convertToString(e, val);
}
-Heap::Object *Value::toObject(ExecutionEngine *e) const
+Heap::Object *Value::toObject(ExecutionEngine *e, Value val)
{
- if (Object *o = objectValue())
- return o->d();
- return RuntimeHelpers::convertToObject(e, *this);
+ return RuntimeHelpers::convertToObject(e, val);
}
uint Value::asArrayLength(bool *ok) const
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 17d0c32853..52d9f23afd 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -51,15 +51,12 @@
//
#include <limits.h>
+#include <cmath>
#include <QtCore/QString>
#include "qv4global_p.h"
#include <private/qv4heap_p.h>
-#if QT_POINTER_SIZE == 8
-#define QV4_USE_64_BIT_VALUE_ENCODING
-#endif
-
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -72,9 +69,7 @@ struct Q_QML_PRIVATE_EXPORT Value
{
private:
/*
- We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.
-
- In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double
+ We use 8 bytes for a value and a different variant of NaN boxing. A Double
NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
processor, and are thus free for us to store other data. We keep pointers in there for
@@ -83,18 +78,14 @@ private:
only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
pointers.)
- On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value
- that will make the number a NaN. The Masks below are used for encoding the other types.
-
- On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
+ We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
used to encode Null/Int/Bool.
- On both 32bit and 64bit, Undefined is encoded as a managed pointer with value 0. This is
- the same as a nullptr.
+ Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
Specific bit-sequences:
0 = always 0
@@ -102,8 +93,6 @@ private:
x = stored value
a,b,c,d = specific bit values, see notes
- 64bit:
-
32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
------------------------------------------------------------------------+--------------
@@ -112,9 +101,9 @@ private:
a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
- 00000000 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null
- 00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
- 00000000 00000011 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+ 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+ 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool
+ 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
Notes:
- a: xor-ed signbit, always 1 for NaN
@@ -128,31 +117,8 @@ private:
- Null, Bool, and Int have bit 48 set, indicating integer-convertible
- xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
any non double results in a NaN
-
- 32bit:
-
- 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
- 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
- ------------------------------------------------------------------------+--------------
- 01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
- 01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
- a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
- xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
- 01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
- 01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null
- 01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
- 01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
-
- Notes:
- - the upper 32 bits are the tag, the lower 32 bits the value
- - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value
- - a: sign bit, always 0 for NaN
- - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value
- - d: stored double value, as long as not *all* of them are 1, because that's a boxed value
- (see above)
- - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined
- and managed), and bit 49 set to 1 (where undefined and managed have it set to 0)
- - Null, Bool, and Int have bit 48 set, indicating integer-convertible
+ - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to
+ 63) are zero. No need to shift.
*/
quint64 _val;
@@ -172,8 +138,9 @@ public:
QML_NEARLY_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
QML_NEARLY_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); }
QML_NEARLY_ALWAYS_INLINE quint32 tag() const { return _val >> 32; }
+ QML_NEARLY_ALWAYS_INLINE void setTag(quint32 tag) { setTagValue(tag, value()); }
-#if defined(QV4_USE_64_BIT_VALUE_ENCODING)
+#if QT_POINTER_SIZE == 8
QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const
{
Heap::Base *b;
@@ -184,7 +151,7 @@ public:
{
memcpy(&_val, &b, 8);
}
-#else // !QV4_USE_64_BIT_VALUE_ENCODING
+#elif QT_POINTER_SIZE == 4
QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const
{
Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32));
@@ -199,6 +166,8 @@ public:
memcpy(&v, &b, 4);
setTagValue(Managed_Type_Internal, v);
}
+#else
+# error "unsupported pointer size"
#endif
QML_NEARLY_ALWAYS_INLINE int int_32() const
@@ -232,24 +201,37 @@ public:
return quint32(value());
}
+ // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
+ // and use negative numbers here
+ enum QuickType {
+ QT_ManagedOrUndefined = 0,
+ QT_ManagedOrUndefined1 = 1,
+ QT_ManagedOrUndefined2 = 2,
+ QT_ManagedOrUndefined3 = 3,
+ QT_Empty = 4,
+ QT_Null = 5,
+ QT_Bool = 6,
+ QT_Int = 7
+ // all other values are doubles
+ };
+
enum Type {
- Undefined_Type,
- Managed_Type,
- Empty_Type,
- Integer_Type,
- Boolean_Type,
- Null_Type,
- Double_Type
+ Undefined_Type = 0,
+ Managed_Type = 1,
+ Empty_Type = 4,
+ Null_Type = 5,
+ Boolean_Type = 6,
+ Integer_Type = 7,
+ Double_Type = 8
};
inline Type type() const {
- if (isUndefined()) return Undefined_Type;
- if (isManaged()) return Managed_Type;
- if (isEmpty()) return Empty_Type;
- if (isInteger()) return Integer_Type;
- if (isBoolean()) return Boolean_Type;
- if (isNull()) return Null_Type;
- Q_ASSERT(isDouble()); return Double_Type;
+ int t = quickType();
+ if (t < QT_Empty)
+ return _val ? Managed_Type : Undefined_Type;
+ if (t > QT_Int)
+ return Double_Type;
+ return static_cast<Type>(t);
}
// Shared between 32-bit and 64-bit encoding
@@ -262,19 +244,18 @@ public:
enum {
IsDouble_Shift = 64-14,
IsManagedOrUndefined_Shift = 64-15,
- IsIntegerConvertible_Shift = 64-16,
- IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift,
- Managed_Type_Internal_64 = 0
+ IsIntegerConvertible_Shift = 64-15,
+ IsIntegerOrBool_Shift = 64-16,
+ QuickType_Shift = 64 - 17
};
static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
enum class ValueTypeInternal_64 {
- Empty = Immediate_Mask_64| 0,
- ConvertibleToInt = Immediate_Mask_64| 0x10000u, // bit 48
- Null = ConvertibleToInt | 0x08000u,
- Boolean = ConvertibleToInt | 0x04000u,
- Integer = ConvertibleToInt | 0x02000u
+ Empty = Immediate_Mask_64 | 0,
+ Null = Immediate_Mask_64 | 0x08000u,
+ Boolean = Immediate_Mask_64 | 0x10000u,
+ Integer = Immediate_Mask_64 | 0x18000u
};
// Used only by 32-bit encoding
@@ -285,50 +266,44 @@ public:
static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit;
enum class ValueTypeInternal_32 {
- Empty = Immediate_Mask_32| 0,
- ConvertibleToInt = Immediate_Mask_32| 0x10000u, // bit 48
- Null = ConvertibleToInt | 0x08000u,
- Boolean = ConvertibleToInt | 0x04000u,
- Integer = ConvertibleToInt | 0x02000u
+ Empty = Immediate_Mask_32 | 0,
+ Null = Immediate_Mask_32 | 0x08000u,
+ Boolean = Immediate_Mask_32 | 0x10000u,
+ Integer = Immediate_Mask_32 | 0x18000u
};
enum {
- Managed_Type_Internal_32 = NotDouble_Mask
+ Managed_Type_Internal = 0
};
-#ifdef QV4_USE_64_BIT_VALUE_ENCODING
- enum {
- Managed_Type_Internal = Managed_Type_Internal_64
- };
- static const quint64 Immediate_Mask = Immediate_Mask_64;
using ValueTypeInternal = ValueTypeInternal_64;
-#else
- enum {
- Managed_Type_Internal = Managed_Type_Internal_32
- };
- static const quint64 Immediate_Mask = Immediate_Mask_32;
- using ValueTypeInternal = ValueTypeInternal_32;
-#endif
+
enum {
NaN_Mask = 0x7ff80000,
};
+ inline quint64 quickType() const { return (_val >> QuickType_Shift); }
+
// used internally in property
inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
- inline bool isNumber() const { return isDouble() || isInteger(); }
+ inline bool isNumber() const { return quickType() >= QT_Int; }
-#ifdef QV4_USE_64_BIT_VALUE_ENCODING
inline bool isUndefined() const { return _val == 0; }
inline bool isDouble() const { return (_val >> IsDouble_Shift); }
- inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); }
+ inline bool isManaged() const { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); }
inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); }
+ inline bool isIntOrBool() const {
+ return (_val >> IsIntegerOrBool_Shift) == 3;
+ }
+
inline bool integerCompatible() const {
- return (_val >> IsIntegerConvertible_Shift) == 3;
+ Q_ASSERT(!isEmpty());
+ return (_val >> IsIntegerConvertible_Shift) == 1;
}
static inline bool integerCompatible(Value a, Value b) {
return a.integerCompatible() && b.integerCompatible();
@@ -337,39 +312,23 @@ public:
return a.isDouble() && b.isDouble();
}
inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
-#else
- inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; }
- inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; }
- inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); }
- inline bool isManagedOrUndefined() const { return tag() == Managed_Type_Internal; }
- inline bool integerCompatible() const { return (tag() & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); }
- static inline bool integerCompatible(Value a, Value b) {
- return ((a.tag() & b.tag()) & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt);
- }
- static inline bool bothDouble(Value a, Value b) {
- return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask;
- }
- inline bool isNaN() const { return (tag() & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; }
-#endif
+
QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
Q_ASSERT(isDouble());
double d;
- quint64 v = _val;
-#ifdef QV4_USE_64_BIT_VALUE_ENCODING
- v ^= NaNEncodeMask;
-#endif
- memcpy(&d, &v, 8);
+ Value v = *this;
+ v._val ^= NaNEncodeMask;
+ memcpy(&d, &v._val, 8);
return d;
}
QML_NEARLY_ALWAYS_INLINE void setDouble(double d) {
memcpy(&_val, &d, 8);
-#ifdef QV4_USE_64_BIT_VALUE_ENCODING
_val ^= NaNEncodeMask;
-#endif
Q_ASSERT(isDouble());
}
inline bool isString() const;
inline bool isObject() const;
+ inline bool isFunctionObject() const;
inline bool isInt32() {
if (tag() == quint32(ValueTypeInternal::Integer))
return true;
@@ -426,14 +385,31 @@ public:
inline int toInt32() const;
inline unsigned int toUInt32() const;
- bool toBoolean() const;
+ bool toBoolean() const {
+ if (integerCompatible())
+ return static_cast<bool>(int_32());
+
+ return toBooleanImpl(*this);
+ }
+ static bool toBooleanImpl(Value val);
double toInteger() const;
inline double toNumber() const;
- double toNumberImpl() const;
+ static double toNumberImpl(Value v);
+ double toNumberImpl() const { return toNumberImpl(*this); }
QString toQStringNoThrow() const;
QString toQString() const;
- Heap::String *toString(ExecutionEngine *e) const;
- Heap::Object *toObject(ExecutionEngine *e) const;
+ Heap::String *toString(ExecutionEngine *e) const {
+ if (isString())
+ return reinterpret_cast<Heap::String *>(m());
+ return toString(e, *this);
+ }
+ static Heap::String *toString(ExecutionEngine *e, Value val);
+ Heap::Object *toObject(ExecutionEngine *e) const {
+ if (isObject())
+ return reinterpret_cast<Heap::Object *>(m());
+ return toObject(e, *this);
+ }
+ static Heap::Object *toObject(ExecutionEngine *e, Value val);
inline bool isPrimitive() const;
inline bool tryIntegerConversion() {
@@ -509,6 +485,13 @@ public:
};
V4_ASSERT_IS_TRIVIAL(Value)
+inline void Value::mark(MarkStack *markStack)
+{
+ Heap::Base *o = heapObject();
+ if (o)
+ o->mark(markStack);
+}
+
inline bool Value::isString() const
{
Heap::Base *b = heapObject();
@@ -520,6 +503,12 @@ inline bool Value::isObject() const
return b && b->vtable()->isObject;
}
+inline bool Value::isFunctionObject() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->vtable()->isFunctionObject;
+}
+
inline bool Value::isPrimitive() const
{
return !isObject();
@@ -538,7 +527,7 @@ inline double Value::toNumber() const
#ifndef V4_BOOTSTRAP
inline uint Value::asArrayIndex() const
{
-#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+#if QT_POINTER_SIZE == 8
if (!isNumber())
return UINT_MAX;
if (isInteger())
@@ -558,8 +547,8 @@ inline uint Value::asArrayIndex() const
inline bool Value::asArrayIndex(uint &idx) const
{
- if (!isDouble()) {
- if (isInteger() && int_32() >= 0) {
+ if (Q_LIKELY(!isDouble())) {
+ if (Q_LIKELY(isInteger() && int_32() >= 0)) {
idx = (uint)int_32();
return true;
}
@@ -593,9 +582,9 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value
using Value::toInt32;
using Value::toUInt32;
- static double toInteger(double fromNumber);
- static int toInt32(double value);
- static unsigned int toUInt32(double value);
+ static double toInteger(double d);
+ static int toInt32(double d);
+ static unsigned int toUInt32(double d);
};
inline Primitive Primitive::undefinedValue()
@@ -658,6 +647,72 @@ inline Primitive Primitive::fromUInt32(uint i)
return v;
}
+struct Double {
+ quint64 d;
+
+ Double(double dbl) {
+ memcpy(&d, &dbl, sizeof(double));
+ }
+
+ int sign() const {
+ return (d >> 63) ? -1 : 1;
+ }
+
+ bool isDenormal() const {
+ return static_cast<int>((d << 1) >> 53) == 0;
+ }
+
+ int exponent() const {
+ return static_cast<int>((d << 1) >> 53) - 1023;
+ }
+
+ quint64 significant() const {
+ quint64 m = (d << 12) >> 12;
+ if (!isDenormal())
+ m |= (static_cast<quint64>(1) << 52);
+ return m;
+ }
+
+ static int toInt32(double d) {
+ int i = static_cast<int>(d);
+ if (i == d)
+ return i;
+ return Double(d).toInt32();
+ }
+
+ int toInt32() {
+ int e = exponent() - 52;
+ if (e < 0) {
+ if (e <= -53)
+ return 0;
+ return sign() * static_cast<int>(significant() >> -e);
+ } else {
+ if (e > 31)
+ return 0;
+ return sign() * (static_cast<int>(significant()) << e);
+ }
+ }
+};
+
+inline double Primitive::toInteger(double d)
+{
+ if (std::isnan(d))
+ return +0;
+ else if (!d || std::isinf(d))
+ return d;
+ return d >= 0 ? std::floor(d) : std::ceil(d);
+}
+
+inline int Primitive::toInt32(double value)
+{
+ return Double::toInt32(value);
+}
+
+inline unsigned int Primitive::toUInt32(double d)
+{
+ return static_cast<uint>(toInt32(d));
+}
+
struct Encode {
static ReturnedValue undefined() {
return Primitive::undefinedValue().rawValue();
@@ -666,33 +721,47 @@ struct Encode {
return Primitive::nullValue().rawValue();
}
- Encode(bool b) {
+ explicit Encode(bool b) {
val = Primitive::fromBoolean(b).rawValue();
}
- Encode(double d) {
+ explicit Encode(double d) {
val = Primitive::fromDouble(d).rawValue();
}
- Encode(int i) {
+ explicit Encode(int i) {
val = Primitive::fromInt32(i).rawValue();
}
- Encode(uint i) {
+ explicit Encode(uint i) {
val = Primitive::fromUInt32(i).rawValue();
}
- Encode(ReturnedValue v) {
+ explicit Encode(ReturnedValue v) {
val = v;
}
+ Encode(Value v) {
+ val = v.rawValue();
+ }
- Encode(Heap::Base *o) {
- Q_ASSERT(o);
+ explicit Encode(Heap::Base *o) {
val = Value::fromHeapObject(o).asReturnedValue();
}
+ explicit Encode(Value *o) {
+ Q_ASSERT(o);
+ val = o->asReturnedValue();
+ }
+
+ static ReturnedValue smallestNumber(double d) {
+ if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d)))
+ return Encode(static_cast<int>(d));
+ else
+ return Encode(d);
+ }
+
operator ReturnedValue() const {
return val;
}
quint64 val;
private:
- Encode(void *);
+ explicit Encode(void *);
};
template<typename T>
@@ -700,24 +769,29 @@ ReturnedValue value_convert(ExecutionEngine *e, const Value &v);
inline int Value::toInt32() const
{
- if (isInteger())
+ if (Q_LIKELY(integerCompatible()))
return int_32();
- double d = isNumber() ? doubleValue() : toNumberImpl();
-
- const double D32 = 4294967296.0;
- const double D31 = D32 / 2.0;
- if ((d >= -D31 && d < D31))
- return static_cast<int>(d);
+ if (Q_LIKELY(isDouble()))
+ return Double::toInt32(doubleValue());
- return Primitive::toInt32(d);
+ return Double::toInt32(toNumberImpl());
}
inline unsigned int Value::toUInt32() const
{
- return (unsigned int)toInt32();
+ return static_cast<unsigned int>(toInt32());
}
+inline double Value::toInteger() const
+{
+ if (integerCompatible())
+ return int_32();
+
+ return Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl());
+}
+
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index f2ff5d307e..00be90b3c2 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -113,17 +113,17 @@ void VariantPrototype::init()
defineDefaultProperty(engine()->id_toString(), method_toString, 0);
}
-void VariantPrototype::method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue VariantPrototype::method_preserve(const BuiltinFunction *, CallData *callData)
{
- Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>());
+ VariantObject *o = callData->thisObject.as<QV4::VariantObject>();
if (o && o->d()->isScarce())
o->d()->addVmePropertyReference();
RETURN_UNDEFINED();
}
-void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue VariantPrototype::method_destroy(const BuiltinFunction *, CallData *callData)
{
- Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>());
+ VariantObject *o = callData->thisObject.as<QV4::VariantObject>();
if (o) {
if (o->d()->isScarce())
o->d()->addVmePropertyReference();
@@ -132,9 +132,10 @@ void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, Cal
RETURN_UNDEFINED();
}
-void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue VariantPrototype::method_toString(const BuiltinFunction *b, CallData *callData)
{
- Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>());
+ ExecutionEngine *v4 = b->engine();
+ VariantObject *o = callData->thisObject.as<QV4::VariantObject>();
if (!o)
RETURN_UNDEFINED();
QString result = o->d()->data().toString();
@@ -143,38 +144,33 @@ void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca
+ QLatin1String(o->d()->data().typeName())
+ QLatin1Char(')');
}
- scope.result = scope.engine->newString(result);
+ return Encode(v4->newString(result));
}
-void VariantPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue VariantPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData)
{
- Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>());
+ VariantObject *o = callData->thisObject.as<QV4::VariantObject>();
if (o) {
QVariant v = o->d()->data();
switch (v.type()) {
case QVariant::Invalid:
- scope.result = Encode::undefined();
- return;
+ return Encode::undefined();
case QVariant::String:
- scope.result = scope.engine->newString(v.toString());
- return;
+ return Encode(b->engine()->newString(v.toString()));
case QVariant::Int:
- scope.result = Encode(v.toInt());
- return;
+ return Encode(v.toInt());
case QVariant::Double:
case QVariant::UInt:
- scope.result = Encode(v.toDouble());
- return;
+ return Encode(v.toDouble());
case QVariant::Bool:
- scope.result = Encode(v.toBool());
- return;
+ return Encode(v.toBool());
default:
if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration)
RETURN_RESULT(Encode(v.toInt()));
break;
}
}
- scope.result = callData->thisObject;
+ return callData->thisObject.asReturnedValue();
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index a7c6fa320c..07b3310e91 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -108,10 +108,10 @@ public:
V4_PROTOTYPE(objectPrototype)
void init();
- static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_preserve(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_destroy(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData);
};
}
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index fdf47e772e..adf43812cf 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -53,19 +53,16 @@
#include <private/qv4regexp_p.h>
#include <private/qv4regexpobject_p.h>
#include <private/qv4string_p.h>
+#include <private/qv4profiling_p.h>
+#include <private/qv4jscall_p.h>
+#include <private/qqmljavascriptexpression_p.h>
#include <iostream>
#include "qv4alloca_p.h"
-#undef DO_TRACE_INSTR // define to enable instruction tracing
+#include <private/qv4jit_p.h>
-#ifdef DO_TRACE_INSTR
-# define TRACE_INSTR(I) qDebug("executing a %s\n", #I);
-# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); qDebug(" %s : %s", #n, buf); }
-#else
-# define TRACE_INSTR(I)
-# define TRACE(n, str, ...)
-#endif // DO_TRACE_INSTR
+#undef COUNT_INSTRUCTIONS
extern "C" {
@@ -146,7 +143,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json);
#if QT_CONFIG(qml_debug)
static int qt_v4BreakpointCount = 0;
-static bool qt_v4IsDebugging = true;
+static bool qt_v4IsDebugging = false;
static bool qt_v4IsStepping = false;
class Breakpoint
@@ -169,14 +166,6 @@ public:
static QVector<Breakpoint> qt_v4Breakpoints;
static Breakpoint qt_v4LastStop;
-static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context)
-{
- if (QV4::Function *function = context->getFunction())
- return function;
- else
- return context->engine()->globalCode;
-}
-
static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function)
{
qt_v4LastStop = bp;
@@ -223,6 +212,7 @@ int qt_v4DebuggerHook(const char *json)
bp.fullName = ob.value(QLatin1String("fullName")).toString();
bp.condition = ob.value(QLatin1String("condition")).toString();
qt_v4Breakpoints.append(bp);
+ qt_v4IsDebugging = true;
return bp.bpNumber;
}
@@ -231,6 +221,7 @@ int qt_v4DebuggerHook(const char *json)
QString fullName = ob.value(QLatin1String("fullName")).toString();
if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) {
qt_v4Breakpoints.removeLast();
+ qt_v4IsDebugging = !qt_v4Breakpoints.isEmpty();
return Success;
}
for (int i = 0; i + 1 < qt_v4Breakpoints.size(); ++i) {
@@ -251,13 +242,13 @@ int qt_v4DebuggerHook(const char *json)
return -NoSuchCommand; // Failure.
}
-static void qt_v4CheckForBreak(QV4::ExecutionContext *context)
+Q_NEVER_INLINE static void qt_v4CheckForBreak(QV4::CppStackFrame *frame)
{
if (!qt_v4IsStepping && !qt_v4Breakpoints.size())
return;
- const int lineNumber = context->d()->lineNumber;
- QV4::Function *function = qt_v4ExtractFunction(context);
+ const int lineNumber = frame->lineNumber();
+ QV4::Function *function = frame->v4Function;
QString engineName = function->sourceFile();
if (engineName.isEmpty())
@@ -287,74 +278,64 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context)
}
}
+Q_NEVER_INLINE static void debug_slowPath(QV4::ExecutionEngine *engine)
+{
+ QV4::Debugging::Debugger *debugger = engine->debugger();
+ if (debugger && debugger->pauseAtNextOpportunity())
+ debugger->maybeBreakAtInstruction();
+ if (qt_v4IsDebugging)
+ qt_v4CheckForBreak(engine->currentStackFrame);
+}
+
#endif // QT_CONFIG(qml_debug)
// End of debugger interface
using namespace QV4;
using namespace QV4::Moth;
-#define MOTH_BEGIN_INSTR_COMMON(I) { \
- const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \
- code += InstrMeta<(int)Instr::I>::Size; \
- Q_UNUSED(instr); \
- TRACE_INSTR(I)
-
-#ifdef MOTH_THREADED_INTERPRETER
-
-# define MOTH_BEGIN_INSTR(I) op_##I: \
- MOTH_BEGIN_INSTR_COMMON(I)
-
-# define MOTH_END_INSTR(I) } \
- genericInstr = reinterpret_cast<const Instr *>(code); \
- goto *jumpTable[genericInstr->common.instructionType]; \
-
-#else
-
-# define MOTH_BEGIN_INSTR(I) \
- case Instr::I: \
- MOTH_BEGIN_INSTR_COMMON(I)
-
-# define MOTH_END_INSTR(I) } \
- continue;
-
-#endif
-
-#ifdef DO_TRACE_INSTR
-Param traceParam(const Param &param)
-{
- if (param.isConstant()) {
- qDebug(" constant\n");
- } else if (param.isArgument()) {
- qDebug(" argument %d@%d\n", param.index, param.scope);
- } else if (param.isLocal()) {
- qDebug(" local %d\n", param.index);
- } else if (param.isTemp()) {
- qDebug(" temp %d\n", param.index);
- } else if (param.isScopedLocal()) {
- qDebug(" temp %d@%d\n", param.index, param.scope);
- } else {
- Q_ASSERT(!"INVALID");
+#ifdef COUNT_INSTRUCTIONS
+static struct InstrCount {
+ InstrCount() {
+ fprintf(stderr, "Counting instructions...\n");
+ for (int i = 0; i < MOTH_NUM_INSTRUCTIONS(); ++i)
+ hits[i] = 0;
+ }
+ ~InstrCount() {
+ fprintf(stderr, "Instruction count:\n");
+#define BLAH(I) \
+ fprintf(stderr, "%llu : %s\n", hits[int(Instr::Type::I)], #I);
+ FOR_EACH_MOTH_INSTR(BLAH)
+ #undef BLAH
+ }
+ quint64 hits[MOTH_NUM_INSTRUCTIONS()];
+ void hit(Instr::Type i) { hits[int(i)]++; }
+} instrCount;
+#endif // COUNT_INSTRUCTIONS
+
+#define MOTH_BEGIN_INSTR_COMMON(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE)
+
+#ifdef COUNT_INSTRUCTIONS
+# define MOTH_BEGIN_INSTR(instr) \
+ MOTH_BEGIN_INSTR_COMMON(instr) \
+ instrCount.hit(Instr::Type::instr);
+#else // !COUNT_INSTRUCTIONS
+# define MOTH_BEGIN_INSTR(instr) \
+ MOTH_BEGIN_INSTR_COMMON(instr)
+#endif // COUNT_INSTRUCTIONS
+
+#ifdef MOTH_COMPUTED_GOTO
+#define MOTH_END_INSTR(instr) \
+ MOTH_DISPATCH() \
+ }
+#else // !MOTH_COMPUTED_GOTO
+#define MOTH_END_INSTR(instr) \
+ continue; \
}
- return param;
-}
-# define VALUE(param) (*VALUEPTR(param))
-# define VALUEPTR(param) (scopes[traceParam(param).scope].values + param.index)
-#else
-# define VALUE(param) (*VALUEPTR(param))
-# define VALUEPTR(param) (scopes[param.scope].values + param.index)
#endif
-// ### add write barrier here
-#define STOREVALUE(param, value) { \
- QV4::ReturnedValue tmp = (value); \
- if (engine->hasException) \
- goto catchException; \
- if (Q_LIKELY(!engine->writeBarrierActive || !scopes[param.scope].base)) { \
- VALUE(param) = tmp; \
- } else { \
- QV4::WriteBarrier::write(engine, scopes[param.scope].base, VALUEPTR(param), QV4::Value::fromReturnedValue(tmp)); \
- } \
-}
+#define STACK_VALUE(temp) stack[temp]
// qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro
#ifdef CHECK_EXCEPTION
@@ -364,617 +345,1038 @@ Param traceParam(const Param &param)
if (engine->hasException) \
goto catchException
-QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code)
+static inline Heap::CallContext *getScope(Value *stack, int level)
{
-#ifdef DO_TRACE_INSTR
- qDebug("Starting VME with context=%p and code=%p", context, code);
-#endif // DO_TRACE_INSTR
+ Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d();
+ while (level > 0) {
+ --level;
+ scope = scope->outer;
+ }
+ Q_ASSERT(scope);
+ return static_cast<Heap::CallContext *>(scope);
+}
- qt_v4ResolvePendingBreakpointsHook();
+static inline const QV4::Value &constant(Function *function, int index)
+{
+ return function->compilationUnit->constants[index];
+}
-#ifdef MOTH_THREADED_INTERPRETER
-#define MOTH_INSTR_ADDR(I, FMT) &&op_##I,
- static void *jumpTable[] = {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR)
- };
-#undef MOTH_INSTR_ADDR
-#endif
- QV4::Value *stack = 0;
- unsigned stackSize = 0;
+static bool compareEqual(Value lhs, Value rhs)
+{
+ redo:
+ if (lhs.asReturnedValue() == rhs.asReturnedValue())
+ return !lhs.isNaN();
+
+ int lt = lhs.quickType();
+ int rt = rhs.quickType();
+ if (rt < lt) {
+ qSwap(lhs, rhs);
+ qSwap(lt, rt);
+ }
- const uchar *exceptionHandler = 0;
+ switch (lt) {
+ case Value::QT_ManagedOrUndefined:
+ if (lhs.isUndefined())
+ return rhs.isNullOrUndefined();
+ Q_FALLTHROUGH();
+ case Value::QT_ManagedOrUndefined1:
+ case Value::QT_ManagedOrUndefined2:
+ case Value::QT_ManagedOrUndefined3:
+ // LHS: Managed
+ switch (rt) {
+ case Value::QT_ManagedOrUndefined:
+ if (rhs.isUndefined())
+ return false;
+ Q_FALLTHROUGH();
+ case Value::QT_ManagedOrUndefined1:
+ case Value::QT_ManagedOrUndefined2:
+ case Value::QT_ManagedOrUndefined3: {
+ // RHS: Managed
+ Heap::Base *l = lhs.m();
+ Heap::Base *r = rhs.m();
+ Q_ASSERT(l);
+ Q_ASSERT(r);
+ if (l->vtable()->isString == r->vtable()->isString)
+ return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
+ if (l->vtable()->isString) {
+ rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT));
+ break;
+ } else {
+ Q_ASSERT(r->vtable()->isString);
+ lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
+ break;
+ }
+ return false;
+ }
+ case Value::QT_Empty:
+ Q_UNREACHABLE();
+ case Value::QT_Null:
+ return false;
+ case Value::QT_Bool:
+ case Value::QT_Int:
+ rhs = Primitive::fromDouble(rhs.int_32());
+ // fall through
+ default: // double
+ if (lhs.m()->vtable()->isString)
+ return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue();
+ else
+ lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
+ }
+ goto redo;
+ case Value::QT_Empty:
+ Q_UNREACHABLE();
+ case Value::QT_Null:
+ return rhs.isNull();
+ case Value::QT_Bool:
+ case Value::QT_Int:
+ switch (rt) {
+ case Value::QT_ManagedOrUndefined:
+ case Value::QT_ManagedOrUndefined1:
+ case Value::QT_ManagedOrUndefined2:
+ case Value::QT_ManagedOrUndefined3:
+ case Value::QT_Empty:
+ case Value::QT_Null:
+ Q_UNREACHABLE();
+ case Value::QT_Bool:
+ case Value::QT_Int:
+ return lhs.int_32() == rhs.int_32();
+ default: // double
+ return lhs.int_32() == rhs.doubleValue();
+ }
+ default: // double
+ Q_ASSERT(rhs.isDouble());
+ return lhs.doubleValue() == rhs.doubleValue();
+ }
+}
- QV4::Scope scope(engine);
- engine->current->lineNumber = -1;
+static bool compareEqualInt(Value &accumulator, Value lhs, int rhs)
+{
+ redo:
+ switch (lhs.quickType()) {
+ case Value::QT_ManagedOrUndefined:
+ if (lhs.isUndefined())
+ return false;
+ Q_FALLTHROUGH();
+ case Value::QT_ManagedOrUndefined1:
+ case Value::QT_ManagedOrUndefined2:
+ case Value::QT_ManagedOrUndefined3:
+ // LHS: Managed
+ if (lhs.m()->vtable()->isString)
+ return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs;
+ accumulator = lhs;
+ lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
+ goto redo;
+ case Value::QT_Empty:
+ Q_UNREACHABLE();
+ case Value::QT_Null:
+ return false;
+ case Value::QT_Bool:
+ case Value::QT_Int:
+ return lhs.int_32() == rhs;
+ default: // double
+ return lhs.doubleValue() == rhs;
+ }
+}
-#ifdef DO_TRACE_INSTR
- qDebug("Starting VME with context=%p and code=%p", context, code);
-#endif // DO_TRACE_INSTR
+#define STORE_IP() frame.instructionPointer = int(code - codeStart);
+#define STORE_ACC() accumulator = acc;
+#define ACC Primitive::fromReturnedValue(acc)
+#define VALUE_TO_INT(i, val) \
+ int i; \
+ do { \
+ if (Q_LIKELY(val.integerCompatible())) { \
+ i = val.int_32(); \
+ } else { \
+ double d; \
+ if (val.isDouble()) \
+ d = val.doubleValue(); \
+ else { \
+ d = val.toNumberImpl(); \
+ CHECK_EXCEPTION; \
+ } \
+ i = Double::toInt32(d); \
+ } \
+ } while (false)
+
+QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
+{
+ qt_v4ResolvePendingBreakpointsHook();
+ ExecutionEngine *engine;
+ Value *stack;
+ CppStackFrame frame;
+ frame.originalArguments = argv;
+ frame.originalArgumentsCount = argc;
+ Function *function;
- // setup lookup scopes
- int scopeDepth = 0;
{
- QV4::Heap::ExecutionContext *scope = engine->current;
- while (scope) {
- ++scopeDepth;
- scope = scope->outer;
+ Heap::ExecutionContext *scope;
+
+ quintptr d = reinterpret_cast<quintptr>(fo);
+ if (d & 0x1) {
+ // we don't have a FunctionObject, but a ExecData
+ ExecData *data = reinterpret_cast<ExecData *>(d - 1);
+ function = data->function;
+ scope = data->scope->d();
+ fo = nullptr;
+ } else {
+ function = fo->function();
+ scope = fo->scope();
}
+
+ engine = function->internalClass->engine;
+
+ stack = engine->jsStackTop;
+ CallData *callData = reinterpret_cast<CallData *>(stack);
+ callData->function = fo ? fo->asReturnedValue() : Encode::undefined();
+ callData->context = scope;
+ callData->accumulator = Encode::undefined();
+ callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue();
+ if (argc > int(function->nFormals))
+ argc = int(function->nFormals);
+ callData->setArgc(argc);
+
+ int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters;
+ engine->jsStackTop += jsStackFrameSize;
+ memcpy(callData->args, argv, argc*sizeof(Value));
+ for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v)
+ *v = Encode::undefined();
+
+ frame.parent = engine->currentStackFrame;
+ frame.v4Function = function;
+ frame.instructionPointer = 0;
+ frame.jsFrame = callData;
+ engine->currentStackFrame = &frame;
}
+ CHECK_STACK_LIMITS(engine);
- struct Scopes {
- QV4::Value *values;
- QV4::Heap::Base *base; // non 0 if a write barrier is required
- };
- Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth));
- {
- scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 };
- // stack gets setup in push instruction
- scopes[1] = { 0, 0 };
- QV4::Heap::ExecutionContext *scope = engine->current;
- int i = 0;
- while (scope) {
- if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) {
- QV4::Heap::SimpleCallContext *cc = static_cast<QV4::Heap::SimpleCallContext *>(scope);
- scopes[2*i + 2] = { cc->callData->args, 0 };
- scopes[2*i + 3] = { 0, 0 };
- } else if (scope->type == QV4::Heap::ExecutionContext::Type_CallContext) {
- QV4::Heap::CallContext *cc = static_cast<QV4::Heap::CallContext *>(scope);
- scopes[2*i + 2] = { cc->callData->args, cc };
- scopes[2*i + 3] = { cc->locals.values, cc };
- } else {
- scopes[2*i + 2] = { 0, 0 };
- scopes[2*i + 3] = { 0, 0 };
- }
- ++i;
- scope = scope->outer;
- }
+ Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
+ QV4::Debugging::Debugger *debugger = engine->debugger();
+
+ const uchar *exceptionHandler = 0;
+
+ QV4::Value &accumulator = frame.jsFrame->accumulator;
+ QV4::ReturnedValue acc = Encode::undefined();
+
+#ifdef V4_ENABLE_JIT
+ static const bool forceInterpreter = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER");
+ if (function->jittedCode == nullptr) {
+ if (ExecutionEngine::canJIT() && debugger == nullptr && !forceInterpreter)
+ QV4::JIT::BaselineJIT(function).generate();
}
+#endif // V4_ENABLE_JIT
+
+ if (debugger)
+ debugger->enteringFunction();
+ if (function->jittedCode != nullptr && debugger == nullptr) {
+ acc = function->jittedCode(&frame, engine);
+ } else {
+ // interpreter
+ const uchar *code = function->codeData;
+ const uchar *codeStart = code;
+
+ MOTH_JUMP_TABLE;
for (;;) {
- const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
-#ifdef MOTH_THREADED_INTERPRETER
- goto *jumpTable[genericInstr->common.instructionType];
-#else
- switch (genericInstr->common.instructionType) {
-#endif
+ MOTH_DISPATCH()
+ Q_UNREACHABLE(); // only reached when the dispatch doesn't jump somewhere
+
+ MOTH_BEGIN_INSTR(LoadConst)
+ acc = constant(function, index).asReturnedValue();
+ MOTH_END_INSTR(LoadConst)
+
+ MOTH_BEGIN_INSTR(LoadNull)
+ acc = Encode::null();
+ MOTH_END_INSTR(LoadNull)
+
+ MOTH_BEGIN_INSTR(LoadZero)
+ acc = Encode(static_cast<int>(0));
+ MOTH_END_INSTR(LoadZero)
- MOTH_BEGIN_INSTR(Move)
- VALUE(instr.result) = VALUE(instr.source);
- MOTH_END_INSTR(Move)
+ MOTH_BEGIN_INSTR(LoadTrue)
+ acc = Encode(true);
+ MOTH_END_INSTR(LoadTrue)
+
+ MOTH_BEGIN_INSTR(LoadFalse)
+ acc = Encode(false);
+ MOTH_END_INSTR(LoadFalse)
+
+ MOTH_BEGIN_INSTR(LoadUndefined)
+ acc = Encode::undefined();
+ MOTH_END_INSTR(LoadUndefined)
+
+ MOTH_BEGIN_INSTR(LoadInt)
+ acc = Encode(value);
+ MOTH_END_INSTR(LoadInt)
MOTH_BEGIN_INSTR(MoveConst)
- VALUE(instr.result) = instr.source;
+ STACK_VALUE(destTemp) = constant(function, constIndex);
MOTH_END_INSTR(MoveConst)
- MOTH_BEGIN_INSTR(SwapTemps)
- qSwap(VALUE(instr.left), VALUE(instr.right));
- MOTH_END_INSTR(MoveTemp)
+ MOTH_BEGIN_INSTR(LoadReg)
+ acc = STACK_VALUE(reg).asReturnedValue();
+ MOTH_END_INSTR(LoadReg)
+
+ MOTH_BEGIN_INSTR(StoreReg)
+ STACK_VALUE(reg) = acc;
+ MOTH_END_INSTR(StoreReg)
+
+ MOTH_BEGIN_INSTR(MoveReg)
+ STACK_VALUE(destReg) = STACK_VALUE(srcReg);
+ MOTH_END_INSTR(MoveReg)
+
+ MOTH_BEGIN_INSTR(LoadLocal)
+ auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ acc = cc->locals[index].asReturnedValue();
+ MOTH_END_INSTR(LoadLocal)
+
+ MOTH_BEGIN_INSTR(StoreLocal)
+ CHECK_EXCEPTION;
+ auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC);
+ MOTH_END_INSTR(StoreLocal)
+
+ MOTH_BEGIN_INSTR(LoadScopedLocal)
+ auto cc = getScope(stack, scope);
+ acc = cc->locals[index].asReturnedValue();
+ MOTH_END_INSTR(LoadScopedLocal)
+
+ MOTH_BEGIN_INSTR(StoreScopedLocal)
+ CHECK_EXCEPTION;
+ auto cc = getScope(stack, scope);
+ QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC);
+ MOTH_END_INSTR(StoreScopedLocal)
MOTH_BEGIN_INSTR(LoadRuntimeString)
-// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
- VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId];
+ acc = function->compilationUnit->runtimeStrings[stringId]->asReturnedValue();
MOTH_END_INSTR(LoadRuntimeString)
MOTH_BEGIN_INSTR(LoadRegExp)
-// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
- Heap::RegExpObject *ro = engine->newRegExpObject(
- static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)
- ->runtimeRegularExpressions[instr.regExpId].as<RegExp>());
- VALUE(instr.result) = ro;
+ acc = Runtime::method_regexpLiteral(engine, regExpId);
MOTH_END_INSTR(LoadRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
- STOREVALUE(instr.result, Runtime::method_closure(engine, instr.value));
+ acc = Runtime::method_closure(engine, value);
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
- TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData());
- STOREVALUE(instr.result, Runtime::method_getActivationProperty(engine, instr.name));
+ STORE_IP();
+ acc = Runtime::method_loadName(engine, name);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadName)
- MOTH_BEGIN_INSTR(GetGlobalLookup)
- QV4::Lookup *l = engine->current->lookups + instr.index;
- STOREVALUE(instr.result, l->globalGetter(l, engine));
- MOTH_END_INSTR(GetGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadGlobalLookup)
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->globalGetter(l, engine);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadGlobalLookup)
+
+ MOTH_BEGIN_INSTR(StoreNameStrict)
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_storeNameStrict(engine, name, accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(StoreNameStrict)
- MOTH_BEGIN_INSTR(StoreName)
- TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData());
- Runtime::method_setActivationProperty(engine, instr.name, VALUE(instr.source));
+ MOTH_BEGIN_INSTR(StoreNameSloppy)
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_storeNameSloppy(engine, name, accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreName)
+ MOTH_END_INSTR(StoreNameSloppy)
MOTH_BEGIN_INSTR(LoadElement)
- STOREVALUE(instr.result, Runtime::method_getElement(engine, VALUE(instr.base), VALUE(instr.index)));
+ STORE_IP();
+ acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index));
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadElement)
- MOTH_BEGIN_INSTR(LoadElementLookup)
- QV4::Lookup *l = engine->current->lookups + instr.lookup;
- STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index)));
- MOTH_END_INSTR(LoadElementLookup)
+ MOTH_BEGIN_INSTR(LoadElementA)
+ STORE_IP();
+ STORE_ACC();
+ acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadElementA)
MOTH_BEGIN_INSTR(StoreElement)
- Runtime::method_setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
+ STORE_IP();
+ STORE_ACC();
+ if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict())
+ engine->throwTypeError();
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
- MOTH_BEGIN_INSTR(StoreElementLookup)
- QV4::Lookup *l = engine->current->lookups + instr.lookup;
- l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreElementLookup)
-
MOTH_BEGIN_INSTR(LoadProperty)
- STOREVALUE(instr.result, Runtime::method_getProperty(engine, VALUE(instr.base), instr.name));
+ STORE_IP();
+ acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadProperty)
+ MOTH_BEGIN_INSTR(LoadPropertyA)
+ STORE_IP();
+ STORE_ACC();
+ acc = Runtime::method_loadProperty(engine, accumulator, name);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadPropertyA)
+
MOTH_BEGIN_INSTR(GetLookup)
- QV4::Lookup *l = engine->current->lookups + instr.index;
- STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base)));
+ STORE_IP();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->getter(l, engine, STACK_VALUE(base));
+ CHECK_EXCEPTION;
MOTH_END_INSTR(GetLookup)
+ MOTH_BEGIN_INSTR(GetLookupA)
+ STORE_IP();
+ STORE_ACC();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->getter(l, engine, accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(GetLookupA)
+
MOTH_BEGIN_INSTR(StoreProperty)
- Runtime::method_setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source));
+ STORE_IP();
+ STORE_ACC();
+ if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict())
+ engine->throwTypeError();
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreProperty)
MOTH_BEGIN_INSTR(SetLookup)
- QV4::Lookup *l = engine->current->lookups + instr.index;
- l->setter(l, engine, VALUE(instr.base), VALUE(instr.source));
+ STORE_IP();
+ STORE_ACC();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict())
+ engine->throwTypeError();
CHECK_EXCEPTION;
MOTH_END_INSTR(SetLookup)
- MOTH_BEGIN_INSTR(StoreQObjectProperty)
- Runtime::method_setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreQObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadQObjectProperty)
- STOREVALUE(instr.result, Runtime::method_getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
- MOTH_END_INSTR(LoadQObjectProperty)
-
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- Runtime::method_setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
+ STORE_ACC();
+ Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreScopeObjectProperty)
MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- STOREVALUE(instr.result, Runtime::method_getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
+ STORE_IP();
+ acc = Runtime::method_loadQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadScopeObjectProperty)
MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- Runtime::method_setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_storeQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreContextObjectProperty)
MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- STOREVALUE(instr.result, Runtime::method_getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
+ STORE_IP();
+ acc = Runtime::method_loadQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadContextObjectProperty)
MOTH_BEGIN_INSTR(LoadIdObject)
- STOREVALUE(instr.result, Runtime::method_getQmlIdObject(engine, VALUE(instr.base), instr.index));
+ STORE_IP();
+ acc = Runtime::method_loadQmlIdObject(engine, STACK_VALUE(base), index);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(LoadIdObject)
- MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty)
- STOREVALUE(instr.result, Runtime::method_getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex));
- MOTH_END_INSTR(LoadAttachedQObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty)
- STOREVALUE(instr.result, Runtime::method_getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
- MOTH_END_INSTR(LoadSingletonQObjectProperty)
-
- MOTH_BEGIN_INSTR(Push)
- TRACE(inline, "stack size: %u", instr.value);
- stackSize = instr.value;
- stack = scope.alloc(stackSize);
- scopes[1].values = stack;
- MOTH_END_INSTR(Push)
-
MOTH_BEGIN_INSTR(CallValue)
-#if 0 //def DO_TRACE_INSTR
- if (Debugging::Debugger *debugger = context->engine()->debugger) {
- if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) {
- if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) {
- QString n = debugger->name(o);
- std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl;
- }
- }
+ STORE_IP();
+ STORE_ACC();
+ Value func = Value::fromReturnedValue(acc);
+ if (Q_UNLIKELY(!func.isFunctionObject())) {
+ acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
+ goto catchException;
}
-#endif // DO_TRACE_INSTR
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_callValue(engine, VALUE(instr.dest), callData));
+ acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_callProperty(engine, instr.name, callData));
+ STORE_IP();
+ acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_callPropertyLookup(engine, instr.lookupIndex, callData));
- MOTH_END_INSTR(CallPropertyLookup)
+ STORE_IP();
+ Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+ // ok to have the value on the stack here
+ Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
+
+ if (Q_UNLIKELY(!f.isFunctionObject())) {
+ acc = engine->throwTypeError();
+ goto catchException;
+ }
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_callQmlScopeObjectProperty(engine, instr.index, callData));
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_callQmlContextObjectProperty(engine, instr.index, callData));
- MOTH_END_INSTR(CallContextObjectProperty)
+ acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallElement)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_callElement(engine, VALUE(instr.index), callData));
+ STORE_IP();
+ acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(CallElement)
- MOTH_BEGIN_INSTR(CallActivationProperty)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_callActivationProperty(engine, instr.name, callData));
- MOTH_END_INSTR(CallActivationProperty)
+ MOTH_BEGIN_INSTR(CallName)
+ STORE_IP();
+ acc = Runtime::method_callName(engine, name, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallName)
+
+ MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
+ STORE_IP();
+ acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallPossiblyDirectEval)
MOTH_BEGIN_INSTR(CallGlobalLookup)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData));
+ STORE_IP();
+ acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc);
+ CHECK_EXCEPTION;
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(SetExceptionHandler)
- exceptionHandler = instr.offset ? ((const uchar *)&instr.offset) + instr.offset : 0;
+ exceptionHandler = offset ? code + offset : nullptr;
MOTH_END_INSTR(SetExceptionHandler)
- MOTH_BEGIN_INSTR(CallBuiltinThrow)
- Runtime::method_throwException(engine, VALUE(instr.arg));
+ MOTH_BEGIN_INSTR(ThrowException)
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_throwException(engine, accumulator);
+ goto catchException;
+ MOTH_END_INSTR(ThrowException)
+
+ MOTH_BEGIN_INSTR(GetException)
+ acc = engine->hasException ? engine->exceptionValue->asReturnedValue()
+ : Primitive::emptyValue().asReturnedValue();
+ engine->hasException = false;
+ MOTH_END_INSTR(HasException)
+
+ MOTH_BEGIN_INSTR(SetException)
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ MOTH_END_INSTR(SetException)
+
+ MOTH_BEGIN_INSTR(PushCatchContext)
+ STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name);
+ MOTH_END_INSTR(PushCatchContext)
+
+ MOTH_BEGIN_INSTR(CreateCallContext)
+ stack[CallData::Context] = ExecutionContext::newCallContext(&frame);
+ MOTH_END_INSTR(CreateCallContext)
+
+ MOTH_BEGIN_INSTR(PushWithContext)
+ STORE_IP();
+ STORE_ACC();
+ accumulator = accumulator.toObject(engine);
+ CHECK_EXCEPTION;
+ STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator);
+ MOTH_END_INSTR(PushWithContext)
+
+ MOTH_BEGIN_INSTR(PopContext)
+ STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
+ MOTH_END_INSTR(PopContext)
+
+ MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ STORE_ACC();
+ acc = Runtime::method_foreachIterator(engine, accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(CallBuiltinThrow)
+ MOTH_END_INSTR(ForeachIteratorObject)
- MOTH_BEGIN_INSTR(CallBuiltinUnwindException)
- STOREVALUE(instr.result, Runtime::method_unwindException(engine));
- MOTH_END_INSTR(CallBuiltinUnwindException)
+ MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ STORE_ACC();
+ acc = Runtime::method_foreachNextPropertyName(accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ForeachNextPropertyName)
+
+ MOTH_BEGIN_INSTR(DeleteMember)
+ if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) {
+ if (function->isStrict()) {
+ STORE_IP();
+ engine->throwTypeError();
+ goto catchException;
+ }
+ acc = Encode(false);
+ } else {
+ acc = Encode(true);
+ }
+ MOTH_END_INSTR(DeleteMember)
+
+ MOTH_BEGIN_INSTR(DeleteSubscript)
+ if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) {
+ if (function->isStrict()) {
+ STORE_IP();
+ engine->throwTypeError();
+ goto catchException;
+ }
+ acc = Encode(false);
+ } else {
+ acc = Encode(true);
+ }
+ MOTH_END_INSTR(DeleteSubscript)
+
+ MOTH_BEGIN_INSTR(DeleteName)
+ if (!Runtime::method_deleteName(engine, name)) {
+ if (function->isStrict()) {
+ STORE_IP();
+ QString n = function->compilationUnit->runtimeStrings[name]->toQString();
+ engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
+ goto catchException;
+ }
+ acc = Encode(false);
+ } else {
+ acc = Encode(true);
+ }
+ MOTH_END_INSTR(DeleteName)
+
+ MOTH_BEGIN_INSTR(TypeofName)
+ acc = Runtime::method_typeofName(engine, name);
+ MOTH_END_INSTR(TypeofName)
+
+ MOTH_BEGIN_INSTR(TypeofValue)
+ STORE_ACC();
+ acc = Runtime::method_typeofValue(engine, accumulator);
+ MOTH_END_INSTR(TypeofValue)
+
+ MOTH_BEGIN_INSTR(DeclareVar)
+ Runtime::method_declareVar(engine, isDeletable, varName);
+ MOTH_END_INSTR(DeclareVar)
+
+ MOTH_BEGIN_INSTR(DefineArray)
+ QV4::Value *arguments = stack + args;
+ acc = Runtime::method_arrayLiteral(engine, arguments, argc);
+ MOTH_END_INSTR(DefineArray)
+
+ MOTH_BEGIN_INSTR(DefineObjectLiteral)
+ QV4::Value *arguments = stack + args;
+ acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags);
+ MOTH_END_INSTR(DefineObjectLiteral)
+
+ MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
+ acc = Runtime::method_createMappedArgumentsObject(engine);
+ MOTH_END_INSTR(CreateMappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ acc = Runtime::method_createUnmappedArgumentsObject(engine);
+ MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(ConvertThisToObject)
+ Value *t = &stack[CallData::This];
+ if (!t->isObject()) {
+ if (t->isNullOrUndefined()) {
+ *t = engine->globalObject->asReturnedValue();
+ } else {
+ *t = t->toObject(engine)->asReturnedValue();
+ CHECK_EXCEPTION;
+ }
+ }
+ MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope)
- Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
- MOTH_END_INSTR(CallBuiltinPushCatchScope)
-
- MOTH_BEGIN_INSTR(CallBuiltinPushScope)
- Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(CallBuiltinPushScope)
-
- MOTH_BEGIN_INSTR(CallBuiltinPopScope)
- Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine));
- MOTH_END_INSTR(CallBuiltinPopScope)
-
- MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject)
- STOREVALUE(instr.result, Runtime::method_foreachIterator(engine, VALUE(instr.arg)));
- MOTH_END_INSTR(CallBuiltinForeachIteratorObject)
-
- MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName)
- STOREVALUE(instr.result, Runtime::method_foreachNextPropertyName(VALUE(instr.arg)));
- MOTH_END_INSTR(CallBuiltinForeachNextPropertyName)
-
- MOTH_BEGIN_INSTR(CallBuiltinDeleteMember)
- STOREVALUE(instr.result, Runtime::method_deleteMember(engine, VALUE(instr.base), instr.member));
- MOTH_END_INSTR(CallBuiltinDeleteMember)
-
- MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript)
- STOREVALUE(instr.result, Runtime::method_deleteElement(engine, VALUE(instr.base), VALUE(instr.index)));
- MOTH_END_INSTR(CallBuiltinDeleteSubscript)
-
- MOTH_BEGIN_INSTR(CallBuiltinDeleteName)
- STOREVALUE(instr.result, Runtime::method_deleteName(engine, instr.name));
- MOTH_END_INSTR(CallBuiltinDeleteName)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty)
- STOREVALUE(instr.result, Runtime::method_typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index));
- MOTH_END_INSTR(CallBuiltinTypeofMember)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty)
- STOREVALUE(instr.result, Runtime::method_typeofContextObjectProperty(engine, VALUE(instr.base), instr.index));
- MOTH_END_INSTR(CallBuiltinTypeofMember)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofMember)
- STOREVALUE(instr.result, Runtime::method_typeofMember(engine, VALUE(instr.base), instr.member));
- MOTH_END_INSTR(CallBuiltinTypeofMember)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript)
- STOREVALUE(instr.result, Runtime::method_typeofElement(engine, VALUE(instr.base), VALUE(instr.index)));
- MOTH_END_INSTR(CallBuiltinTypeofSubscript)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofName)
- STOREVALUE(instr.result, Runtime::method_typeofName(engine, instr.name));
- MOTH_END_INSTR(CallBuiltinTypeofName)
-
- MOTH_BEGIN_INSTR(CallBuiltinTypeofValue)
- STOREVALUE(instr.result, Runtime::method_typeofValue(engine, VALUE(instr.value)));
- MOTH_END_INSTR(CallBuiltinTypeofValue)
-
- MOTH_BEGIN_INSTR(CallBuiltinDeclareVar)
- Runtime::method_declareVar(engine, instr.isDeletable, instr.varName);
- MOTH_END_INSTR(CallBuiltinDeclareVar)
-
- MOTH_BEGIN_INSTR(CallBuiltinDefineArray)
- Q_ASSERT(instr.args + instr.argc <= stackSize);
- QV4::Value *args = stack + instr.args;
- STOREVALUE(instr.result, Runtime::method_arrayLiteral(engine, args, instr.argc));
- MOTH_END_INSTR(CallBuiltinDefineArray)
-
- MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral)
- QV4::Value *args = stack + instr.args;
- STOREVALUE(instr.result, Runtime::method_objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags));
- MOTH_END_INSTR(CallBuiltinDefineObjectLiteral)
-
- MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject)
- STOREVALUE(instr.result, Runtime::method_setupArgumentsObject(engine));
- MOTH_END_INSTR(CallBuiltinSetupArgumentsObject)
-
- MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject)
- Runtime::method_convertThisToObject(engine);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(CallBuiltinConvertThisToObject)
-
- MOTH_BEGIN_INSTR(CreateValue)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_constructValue(engine, VALUE(instr.func), callData));
- MOTH_END_INSTR(CreateValue)
-
- MOTH_BEGIN_INSTR(CreateProperty)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_constructProperty(engine, instr.name, callData));
- MOTH_END_INSTR(CreateProperty)
-
- MOTH_BEGIN_INSTR(ConstructPropertyLookup)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::method_constructPropertyLookup(engine, instr.index, callData));
- MOTH_END_INSTR(ConstructPropertyLookup)
-
- MOTH_BEGIN_INSTR(CreateActivationProperty)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_constructActivationProperty(engine, instr.name, callData));
- MOTH_END_INSTR(CreateActivationProperty)
-
- MOTH_BEGIN_INSTR(ConstructGlobalLookup)
- Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
- QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = quint32(Value::ValueTypeInternal::Integer);
- callData->argc = instr.argc;
- callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::method_constructGlobalLookup(engine, instr.index, callData));
- MOTH_END_INSTR(ConstructGlobalLookup)
+ MOTH_BEGIN_INSTR(Construct)
+ STORE_IP();
+ acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Construct)
MOTH_BEGIN_INSTR(Jump)
- code = ((const uchar *)&instr.offset) + instr.offset;
+ code += offset;
MOTH_END_INSTR(Jump)
- MOTH_BEGIN_INSTR(JumpEq)
- bool cond = VALUEPTR(instr.condition)->toBoolean();
- TRACE(condition, "%s", cond ? "TRUE" : "FALSE");
- if (cond)
- code = ((const uchar *)&instr.offset) + instr.offset;
- MOTH_END_INSTR(JumpEq)
+ MOTH_BEGIN_INSTR(JumpTrue)
+ if (Q_LIKELY(ACC.integerCompatible())) {
+ if (ACC.int_32())
+ code += offset;
+ } else {
+ if (ACC.toBoolean())
+ code += offset;
+ }
+ MOTH_END_INSTR(JumpTrue)
+
+ MOTH_BEGIN_INSTR(JumpFalse)
+ if (Q_LIKELY(ACC.integerCompatible())) {
+ if (!ACC.int_32())
+ code += offset;
+ } else {
+ if (!ACC.toBoolean())
+ code += offset;
+ }
+ MOTH_END_INSTR(JumpFalse)
+
+ MOTH_BEGIN_INSTR(CmpEqNull)
+ acc = Encode(ACC.isNullOrUndefined());
+ MOTH_END_INSTR(CmpEqNull)
+
+ MOTH_BEGIN_INSTR(CmpNeNull)
+ acc = Encode(!ACC.isNullOrUndefined());
+ MOTH_END_INSTR(CmpNeNull)
+
+ MOTH_BEGIN_INSTR(CmpEqInt)
+ if (ACC.isIntOrBool()) {
+ acc = Encode(ACC.int_32() == lhs);
+ } else {
+ STORE_ACC();
+ acc = Encode(compareEqualInt(accumulator, ACC, lhs));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpEqInt)
+
+ MOTH_BEGIN_INSTR(CmpNeInt)
+ if (ACC.isIntOrBool()) {
+ acc = Encode(bool(ACC.int_32() != lhs));
+ } else {
+ STORE_ACC();
+ acc = Encode(!compareEqualInt(accumulator, ACC, lhs));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpNeInt)
+
+ MOTH_BEGIN_INSTR(CmpEq)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.asReturnedValue() == ACC.asReturnedValue())) {
+ acc = Encode(!ACC.isNaN());
+ } else if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(left.int_32() == ACC.int_32());
+ } else {
+ STORE_ACC();
+ acc = Encode(compareEqual(left, accumulator));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNe)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(bool(left.int_32() != ACC.int_32()));
+ } else {
+ STORE_ACC();
+ acc = Encode(!compareEqual(left, accumulator));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpNe)
+
+ MOTH_BEGIN_INSTR(CmpGt)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(left.int_32() > ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() > ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator)));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpGt)
+
+ MOTH_BEGIN_INSTR(CmpGe)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(left.int_32() >= ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() >= ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator)));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpGe)
+
+ MOTH_BEGIN_INSTR(CmpLt)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(left.int_32() < ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() < ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator)));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpLt)
+
+ MOTH_BEGIN_INSTR(CmpLe)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(left.isInteger() && ACC.isInteger())) {
+ acc = Encode(left.int_32() <= ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() <= ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator)));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpLe)
+
+ MOTH_BEGIN_INSTR(CmpStrictEqual)
+ if (STACK_VALUE(lhs).rawValue() == ACC.rawValue() && !ACC.isNaN()) {
+ acc = Encode(true);
+ } else {
+ STORE_ACC();
+ acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)));
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(CmpStrictEqual)
+
+ MOTH_BEGIN_INSTR(CmpStrictNotEqual)
+ if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) {
+ STORE_ACC();
+ acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator));
+ CHECK_EXCEPTION;
+ } else {
+ acc = Encode(false);
+ }
+ MOTH_END_INSTR(CmpStrictNotEqual)
+
+ MOTH_BEGIN_INSTR(CmpIn)
+ STORE_ACC();
+ acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CmpIn)
+
+ MOTH_BEGIN_INSTR(CmpInstanceOf)
+ // 11.8.6, 5: rval must be an Object
+ const Object *rhs = Primitive::fromReturnedValue(acc).as<Object>();
+ if (Q_UNLIKELY(!rhs)) {
+ acc = engine->throwTypeError();
+ goto catchException;
+ }
+
+ // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
+ acc = rhs->instanceOf(STACK_VALUE(lhs));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CmpInstanceOf)
+
+ MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
+ if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
+ code += offset;
+ MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
- MOTH_BEGIN_INSTR(JumpNe)
- bool cond = VALUEPTR(instr.condition)->toBoolean();
- TRACE(condition, "%s", cond ? "TRUE" : "FALSE");
- if (!cond)
- code = ((const uchar *)&instr.offset) + instr.offset;
- MOTH_END_INSTR(JumpNe)
+ MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
+ if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
+ code += offset;
+ MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
MOTH_BEGIN_INSTR(UNot)
- STOREVALUE(instr.result, Runtime::method_uNot(VALUE(instr.source)));
+ if (ACC.integerCompatible()) {
+ acc = Encode(!static_cast<bool>(ACC.int_32()));
+ } else {
+ acc = Encode(!Value::toBooleanImpl(ACC));
+ }
MOTH_END_INSTR(UNot)
- MOTH_BEGIN_INSTR(UNotBool)
- bool b = VALUE(instr.source).booleanValue();
- VALUE(instr.result) = QV4::Encode(!b);
- MOTH_END_INSTR(UNotBool)
-
MOTH_BEGIN_INSTR(UPlus)
- STOREVALUE(instr.result, Runtime::method_uPlus(VALUE(instr.source)));
+ if (Q_UNLIKELY(!ACC.isNumber())) {
+ acc = Encode(ACC.toNumberImpl());
+ CHECK_EXCEPTION;
+ }
MOTH_END_INSTR(UPlus)
MOTH_BEGIN_INSTR(UMinus)
- STOREVALUE(instr.result, Runtime::method_uMinus(VALUE(instr.source)));
+ if (Q_LIKELY(ACC.integerCompatible())) {
+ int a = ACC.int_32();
+ if (a == 0 || a == std::numeric_limits<int>::min()) {
+ acc = Encode(-static_cast<double>(a));
+ } else {
+ acc = sub_int32(0, ACC.int_32());
+ }
+ } else if (ACC.isDouble()) {
+ acc ^= (1ull << 63); // simply flip sign bit
+ } else {
+ acc = Encode(-ACC.toNumberImpl());
+ CHECK_EXCEPTION;
+ }
MOTH_END_INSTR(UMinus)
MOTH_BEGIN_INSTR(UCompl)
- STOREVALUE(instr.result, Runtime::method_complement(VALUE(instr.source)));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(~a);
MOTH_END_INSTR(UCompl)
- MOTH_BEGIN_INSTR(UComplInt)
- VALUE(instr.result) = QV4::Encode((int)~VALUE(instr.source).integerValue());
- MOTH_END_INSTR(UComplInt)
-
MOTH_BEGIN_INSTR(Increment)
- STOREVALUE(instr.result, Runtime::method_increment(VALUE(instr.source)));
+ if (Q_LIKELY(ACC.integerCompatible())) {
+ acc = add_int32(ACC.int_32(), 1);
+ } else if (ACC.isDouble()) {
+ acc = QV4::Encode(ACC.doubleValue() + 1.);
+ } else {
+ acc = Encode(ACC.toNumberImpl() + 1.);
+ CHECK_EXCEPTION;
+ }
MOTH_END_INSTR(Increment)
MOTH_BEGIN_INSTR(Decrement)
- STOREVALUE(instr.result, Runtime::method_decrement(VALUE(instr.source)));
+ if (Q_LIKELY(ACC.integerCompatible())) {
+ acc = sub_int32(ACC.int_32(), 1);
+ } else if (ACC.isDouble()) {
+ acc = QV4::Encode(ACC.doubleValue() - 1.);
+ } else {
+ acc = Encode(ACC.toNumberImpl() - 1.);
+ CHECK_EXCEPTION;
+ }
MOTH_END_INSTR(Decrement)
- MOTH_BEGIN_INSTR(Binop)
- QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu]));
- STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs)));
- MOTH_END_INSTR(Binop)
-
MOTH_BEGIN_INSTR(Add)
- STOREVALUE(instr.result, Runtime::method_add(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
+ acc = add_int32(left.int_32(), ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() + ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Runtime::method_add(engine, left, accumulator);
+ CHECK_EXCEPTION;
+ }
MOTH_END_INSTR(Add)
+ MOTH_BEGIN_INSTR(Sub)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
+ acc = sub_int32(left.int_32(), ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() - ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Runtime::method_sub(left, accumulator);
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(Sub)
+
+ MOTH_BEGIN_INSTR(Mul)
+ const Value left = STACK_VALUE(lhs);
+ if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
+ acc = mul_int32(left.int_32(), ACC.int_32());
+ } else if (left.isNumber() && ACC.isNumber()) {
+ acc = Encode(left.asDouble() * ACC.asDouble());
+ } else {
+ STORE_ACC();
+ acc = Runtime::method_mul(left, accumulator);
+ CHECK_EXCEPTION;
+ }
+ MOTH_END_INSTR(Mul)
+
+ MOTH_BEGIN_INSTR(Div)
+ STORE_ACC();
+ acc = Runtime::method_div(STACK_VALUE(lhs), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Div)
+
+ MOTH_BEGIN_INSTR(Mod)
+ STORE_ACC();
+ acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Mod)
+
MOTH_BEGIN_INSTR(BitAnd)
- STOREVALUE(instr.result, Runtime::method_bitAnd(VALUE(instr.lhs), VALUE(instr.rhs)));
+ VALUE_TO_INT(l, STACK_VALUE(lhs));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l & a);
MOTH_END_INSTR(BitAnd)
MOTH_BEGIN_INSTR(BitOr)
- STOREVALUE(instr.result, Runtime::method_bitOr(VALUE(instr.lhs), VALUE(instr.rhs)));
+ VALUE_TO_INT(l, STACK_VALUE(lhs));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l | a);
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXor)
- STOREVALUE(instr.result, Runtime::method_bitXor(VALUE(instr.lhs), VALUE(instr.rhs)));
+ VALUE_TO_INT(l, STACK_VALUE(lhs));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l ^ a);
MOTH_END_INSTR(BitXor)
+ MOTH_BEGIN_INSTR(UShr)
+ uint l = STACK_VALUE(lhs).toUInt32();
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l >> uint(a & 0x1f));
+ MOTH_END_INSTR(UShr)
+
MOTH_BEGIN_INSTR(Shr)
- STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> (VALUEPTR(instr.rhs)->toInt32() & 0x1f))));
+ VALUE_TO_INT(l, STACK_VALUE(lhs));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l >> (a & 0x1f));
MOTH_END_INSTR(Shr)
MOTH_BEGIN_INSTR(Shl)
- STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << (VALUEPTR(instr.rhs)->toInt32() & 0x1f))));
+ VALUE_TO_INT(l, STACK_VALUE(lhs));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(l << (a & 0x1f));
MOTH_END_INSTR(Shl)
MOTH_BEGIN_INSTR(BitAndConst)
- int lhs = VALUEPTR(instr.lhs)->toInt32();
- STOREVALUE(instr.result, QV4::Encode((int)(lhs & instr.rhs)));
- MOTH_END_INSTR(BitAnd)
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(a & rhs);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(BitAndConst)
MOTH_BEGIN_INSTR(BitOrConst)
- int lhs = VALUEPTR(instr.lhs)->toInt32();
- STOREVALUE(instr.result, QV4::Encode((int)(lhs | instr.rhs)));
- MOTH_END_INSTR(BitOr)
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(a | rhs);
+ MOTH_END_INSTR(BitOrConst)
MOTH_BEGIN_INSTR(BitXorConst)
- int lhs = VALUEPTR(instr.lhs)->toInt32();
- STOREVALUE(instr.result, QV4::Encode((int)(lhs ^ instr.rhs)));
- MOTH_END_INSTR(BitXor)
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(a ^ rhs);
+ MOTH_END_INSTR(BitXorConst)
+
+ MOTH_BEGIN_INSTR(UShrConst)
+ acc = Encode(ACC.toUInt32() >> uint(rhs));
+ MOTH_END_INSTR(UShrConst)
MOTH_BEGIN_INSTR(ShrConst)
- STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> instr.rhs)));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(a >> rhs);
MOTH_END_INSTR(ShrConst)
MOTH_BEGIN_INSTR(ShlConst)
- STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << instr.rhs)));
+ VALUE_TO_INT(a, ACC);
+ acc = Encode(a << rhs);
MOTH_END_INSTR(ShlConst)
- MOTH_BEGIN_INSTR(Mul)
- STOREVALUE(instr.result, Runtime::method_mul(VALUE(instr.lhs), VALUE(instr.rhs)));
- MOTH_END_INSTR(Mul)
-
- MOTH_BEGIN_INSTR(Sub)
- STOREVALUE(instr.result, Runtime::method_sub(VALUE(instr.lhs), VALUE(instr.rhs)));
- MOTH_END_INSTR(Sub)
-
- MOTH_BEGIN_INSTR(BinopContext)
- QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu]));
- STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
- MOTH_END_INSTR(BinopContext)
-
MOTH_BEGIN_INSTR(Ret)
-// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData());
- return VALUE(instr.result).asReturnedValue();
+ goto functionExit;
MOTH_END_INSTR(Ret)
#if QT_CONFIG(qml_debug)
MOTH_BEGIN_INSTR(Debug)
- engine->current->lineNumber = instr.lineNumber;
- QV4::Debugging::Debugger *debugger = engine->debugger();
- if (debugger && debugger->pauseAtNextOpportunity())
- debugger->maybeBreakAtInstruction();
- if (qt_v4IsDebugging)
- qt_v4CheckForBreak(engine->currentContext);
+ STORE_IP();
+ debug_slowPath(engine);
MOTH_END_INSTR(Debug)
-
- MOTH_BEGIN_INSTR(Line)
- engine->current->lineNumber = instr.lineNumber;
- if (qt_v4IsDebugging)
- qt_v4CheckForBreak(engine->currentContext);
- MOTH_END_INSTR(Line)
#endif // QT_CONFIG(qml_debug)
- MOTH_BEGIN_INSTR(LoadThis)
- VALUE(instr.result) = engine->currentContext->thisObject();
- MOTH_END_INSTR(LoadThis)
-
MOTH_BEGIN_INSTR(LoadQmlContext)
- VALUE(instr.result) = Runtime::method_getQmlContext(static_cast<QV4::NoThrowEngine*>(engine));
+ STACK_VALUE(result) = Runtime::method_loadQmlContext(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlContext)
MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- VALUE(instr.result) = Runtime::method_getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
+ STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
MOTH_BEGIN_INSTR(LoadQmlSingleton)
- VALUE(instr.result) = Runtime::method_getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
+ acc = Runtime::method_loadQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), name);
MOTH_END_INSTR(LoadQmlSingleton)
-#ifdef MOTH_THREADED_INTERPRETER
- // nothing to do
-#else
- default:
- qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType);
- break;
- }
-#endif
-
- Q_ASSERT(false);
catchException:
Q_ASSERT(engine->hasException);
- if (!exceptionHandler)
- return QV4::Encode::undefined();
+ if (!exceptionHandler) {
+ acc = Encode::undefined();
+ goto functionExit;
+ }
code = exceptionHandler;
}
-}
+ }
-QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code)
-{
- VME vme;
- QV4::Debugging::Debugger *debugger = engine->debugger();
- if (debugger)
- debugger->enteringFunction();
- QV4::ReturnedValue retVal = vme.run(engine, code);
- if (debugger)
- debugger->leavingFunction(retVal);
- return retVal;
+functionExit:
+ if (QV4::Debugging::Debugger *debugger = engine->debugger())
+ debugger->leavingFunction(ACC.asReturnedValue());
+ engine->currentStackFrame = frame.parent;
+ engine->jsStackTop = stack;
+
+ return acc;
}
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index 8d46207f2b..dbf9ed3550 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -52,8 +52,6 @@
//
#include <private/qv4global_p.h>
-#include <private/qv4runtime_p.h>
-#include <private/qv4instr_moth_p.h>
QT_REQUIRE_CONFIG(qml_interpreter);
@@ -65,10 +63,17 @@ namespace Moth {
class VME
{
public:
- static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *);
-
-private:
- QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code);
+ struct ExecData {
+ QV4::Function *function;
+ const QV4::ExecutionContext *scope;
+ };
+ static inline
+ QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
+ ExecData data{v4Function, context};
+ quintptr d = reinterpret_cast<quintptr>(&data) | 0x1;
+ return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc);
+ }
+ static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
};
} // namespace Moth
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index f00ce4283c..363e891d5c 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -75,7 +75,6 @@ struct InternalClass;
struct VTable
{
const VTable * const parent;
- const quint64 markTable;
uint inlinePropertyOffset : 16;
uint nInlineProperties : 16;
uint isExecutionContext : 1;
@@ -97,7 +96,7 @@ namespace Heap {
struct Q_QML_EXPORT Base {
void *operator new(size_t) = delete;
- static Q_CONSTEXPR quint64 markTable = 0;
+ static void markObjects(Heap::Base *, MarkStack *) {}
InternalClass *internalClass;
@@ -131,7 +130,9 @@ struct Q_QML_EXPORT Base {
return Chunk::testBit(c->objectBitmap, h - c->realBase());
}
- inline void markChildren(MarkStack *markStack);
+ inline void markChildren(MarkStack *markStack) {
+ vtable()->markObjects(this, markStack);
+ }
void *operator new(size_t, Managed *m) { return m; }
void *operator new(size_t, Heap::Base *m) { return m; }
@@ -184,6 +185,22 @@ Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE);
}
+inline
+void Heap::Base::mark(QV4::MarkStack *markStack)
+{
+ Q_ASSERT(inUse());
+ const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
+ Chunk *c = h->chunk();
+ size_t index = h - c->realBase();
+ Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index));
+ quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index);
+ quintptr bit = Chunk::bitForIndex(index);
+ if (!(*bitmap & bit)) {
+ *bitmap |= bit;
+ markStack->push(this);
+ }
+}
+
#ifdef QT_NO_QOBJECT
template <class T>
struct QQmlQPointer {
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index de97918fb0..3ea7908e9d 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -279,64 +279,6 @@ QString binary(quintptr) { return QString(); }
#define SDUMP if (1) ; else qDebug
#endif
-void Heap::Base::markChildren(MarkStack *markStack)
-{
- if (vtable()->markObjects)
- vtable()->markObjects(this, markStack);
- if (quint64 m = vtable()->markTable) {
-// qDebug() << "using mark table:" << hex << m << "for" << h;
- void **mem = reinterpret_cast<void **>(this);
- while (m) {
- MarkFlags mark = static_cast<MarkFlags>(m & 3);
- switch (mark) {
- case Mark_NoMark:
- break;
- case Mark_Value:
-// qDebug() << "marking value at " << mem;
- reinterpret_cast<Value *>(mem)->mark(markStack);
- break;
- case Mark_Pointer: {
-// qDebug() << "marking pointer at " << mem;
- Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem);
- if (p)
- p->mark(markStack);
- break;
- }
- case Mark_ValueArray: {
- Q_ASSERT(m == Mark_ValueArray);
-// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h));
- ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem);
- Value *v = a->values;
- const Value *end = v + a->alloc;
- if (a->alloc > 32*1024) {
- // drain from time to time to avoid overflows in the js stack
- Heap::Base **currentBase = markStack->top;
- while (v < end) {
- v->mark(markStack);
- ++v;
- if (markStack->top >= currentBase + 32*1024) {
- Heap::Base **oldBase = markStack->base;
- markStack->base = currentBase;
- markStack->drain();
- markStack->base = oldBase;
- }
- }
- } else {
- while (v < end) {
- v->mark(markStack);
- ++v;
- }
- }
- break;
- }
- }
-
- m >>= 2;
- ++mem;
- }
- }
-}
-
// Stores a classname -> freed count mapping.
typedef QHash<const char*, int> MMStatsHash;
Q_GLOBAL_STATIC(MMStatsHash, freedObjectStatsGlobal)
@@ -553,51 +495,6 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
#endif
}
-
-template<typename T>
-StackAllocator<T>::StackAllocator(ChunkAllocator *chunkAlloc)
- : chunkAllocator(chunkAlloc)
-{
- chunks.push_back(chunkAllocator->allocate());
- firstInChunk = chunks.back()->first();
- nextFree = firstInChunk;
- lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
-}
-
-template<typename T>
-void StackAllocator<T>::freeAll()
-{
- for (auto c : chunks)
- chunkAllocator->free(c);
-}
-
-template<typename T>
-void StackAllocator<T>::nextChunk() {
- Q_ASSERT(nextFree == lastInChunk);
- ++currentChunk;
- if (currentChunk >= chunks.size()) {
- Chunk *newChunk = chunkAllocator->allocate();
- chunks.push_back(newChunk);
- }
- firstInChunk = chunks.at(currentChunk)->first();
- nextFree = firstInChunk;
- lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
-}
-
-template<typename T>
-void QV4::StackAllocator<T>::prevChunk() {
- Q_ASSERT(nextFree == firstInChunk);
- Q_ASSERT(chunks.at(currentChunk) == nextFree->chunk());
- Q_ASSERT(currentChunk > 0);
- --currentChunk;
- firstInChunk = chunks.at(currentChunk)->first();
- lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
- nextFree = lastInChunk;
-}
-
-template struct StackAllocator<Heap::CallContext>;
-
-
HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) {
Q_ASSERT((size % Chunk::SlotSize) == 0);
size_t slotsRequired = size >> Chunk::SlotSizeShift;
@@ -836,7 +733,6 @@ void HugeItemAllocator::freeAll()
MemoryManager::MemoryManager(ExecutionEngine *engine)
: engine(engine)
, chunkAllocator(new ChunkAllocator)
- , stackAllocator(chunkAllocator)
, blockAllocator(chunkAllocator)
, hugeItemAllocator(chunkAllocator)
, m_persistentValues(new PersistentValueStorage(engine))
@@ -1261,7 +1157,6 @@ MemoryManager::~MemoryManager()
sweep(/*lastSweep*/true);
blockAllocator.freeAll();
hugeItemAllocator.freeAll();
- stackAllocator.freeAll();
delete m_weakValues;
#ifdef V4_USE_VALGRIND
@@ -1303,9 +1198,11 @@ void MemoryManager::collectFromJSStack(MarkStack *markStack) const
Value *top = engine->jsStackTop;
while (v < top) {
Managed *m = v->managed();
- if (m && m->inUse())
+ if (m) {
+ Q_ASSERT(m->inUse());
// Skip pointers to already freed objects, they are bogus as well
m->mark(markStack);
+ }
++v;
}
}
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index 17957d0cd6..921fea3956 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -72,51 +72,6 @@ namespace QV4 {
struct ChunkAllocator;
-template<typename T>
-struct StackAllocator {
- Q_STATIC_ASSERT(sizeof(T) < Chunk::DataSize);
- static const uint requiredSlots = (sizeof(T) + sizeof(HeapItem) - 1)/sizeof(HeapItem);
-
- StackAllocator(ChunkAllocator *chunkAlloc);
-
- T *allocate() {
- HeapItem *m = nextFree;
- if (Q_UNLIKELY(nextFree == lastInChunk)) {
- nextChunk();
- } else {
- nextFree += requiredSlots;
- }
-#if MM_DEBUG || !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
- Chunk *c = m->chunk();
- Chunk::setBit(c->objectBitmap, m - c->realBase());
-#endif
- return m->as<T>();
- }
- void free() {
- if (Q_UNLIKELY(nextFree == firstInChunk)) {
- prevChunk();
- } else {
- nextFree -= requiredSlots;
- }
-#if MM_DEBUG || !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
- Chunk *c = nextFree->chunk();
- Chunk::clearBit(c->objectBitmap, nextFree - c->realBase());
-#endif
- }
-
- void nextChunk();
- void prevChunk();
-
- void freeAll();
-
- ChunkAllocator *chunkAllocator;
- HeapItem *nextFree = 0;
- HeapItem *firstInChunk = 0;
- HeapItem *lastInChunk = 0;
- std::vector<Chunk *> chunks;
- uint currentChunk = 0;
-};
-
struct BlockAllocator {
BlockAllocator(ChunkAllocator *chunkAllocator)
: chunkAllocator(chunkAllocator)
@@ -211,19 +166,6 @@ public:
Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size)
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
- QV4::Heap::CallContext *allocSimpleCallContext()
- {
- Heap::CallContext *ctxt = stackAllocator.allocate();
- memset(ctxt, 0, sizeof(Heap::SimpleCallContext));
- ctxt->internalClass = SimpleCallContext::defaultInternalClass(engine);
- Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable);
- ctxt->init();
- return ctxt;
-
- }
- void freeSimpleCallContext()
- { stackAllocator.free(); }
-
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size)
{
@@ -237,6 +179,18 @@ public:
return static_cast<typename ManagedType::Data *>(o);
}
+ template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
+ {
+ V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data)
+ size = align(size);
+ Heap::Base *o = allocData(size);
+ o->internalClass = ic;
+ Q_ASSERT(o->internalClass && o->internalClass->vtable);
+ Q_ASSERT(ic->vtable == ManagedType::staticVTable());
+ return static_cast<typename ManagedType::Data *>(o);
+ }
+
template <typename ObjectType>
typename ObjectType::Data *allocateObject(InternalClass *ic)
{
@@ -465,7 +419,6 @@ private:
public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
- StackAllocator<Heap::CallContext> stackAllocator;
BlockAllocator blockAllocator;
HugeItemAllocator hugeItemAllocator;
PersistentValueStorage *m_persistentValues;
diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h
index 328797fb5e..6d911e69f6 100644
--- a/src/qml/memory/qv4mmdefs_p.h
+++ b/src/qml/memory/qv4mmdefs_p.h
@@ -287,32 +287,8 @@ struct MarkStack {
};
-// Some helper classes and macros to automate the generation of our
-// tables used for marking objects
-
-enum MarkFlags {
- Mark_NoMark = 0,
- Mark_Value = 1,
- Mark_Pointer = 2,
- Mark_ValueArray = 3
-};
-
-template <typename T>
-struct MarkFlagEvaluator {
- static Q_CONSTEXPR quint64 value = 0;
-};
-template <typename T, size_t o>
-struct MarkFlagEvaluator<Heap::Pointer<T, o>> {
- static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Pointer) << (2*o / sizeof(quintptr));
-};
-template <size_t o>
-struct MarkFlagEvaluator<ValueArray<o>> {
- static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_ValueArray) << (2*o / sizeof(quintptr));
-};
-template <size_t o>
-struct MarkFlagEvaluator<HeapValue<o>> {
- static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Value) << (2 *o / sizeof(quintptr));
-};
+// Some helper to automate the generation of our
+// functions used for marking objects
#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION(c, gcType, type, name) \
HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_##gcType(c, type, name)
@@ -334,25 +310,42 @@ struct MarkFlagEvaluator<HeapValue<o>> {
#define HEAP_OBJECT_MEMBER_EXPANSION_ValueArray(c, type, name) \
type<offsetof(c##OffsetStruct, name) + baseOffset> name;
-#define HEAP_OBJECT_MARK_EXPANSION(class, gcType, type, name) \
- MarkFlagEvaluator<decltype(class::name)>::value |
+#define HEAP_OBJECT_MARKOBJECTS_EXPANSION(c, gcType, type, name) \
+ HEAP_OBJECT_MARKOBJECTS_EXPANSION_##gcType(c, type, name)
+#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_Pointer(c, type, name) \
+ if (o->name) o->name.heapObject()->mark(stack);
+#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_NoMark(c, type, name)
+#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_HeapValue(c, type, name) \
+ o->name.mark(stack);
+#define HEAP_OBJECT_MARKOBJECTS_EXPANSION_ValueArray(c, type, name) \
+ o->name.mark(stack);
-#define DECLARE_HEAP_OBJECT(name, base) \
+
+#define DECLARE_HEAP_OBJECT_BASE(name, base) \
struct name##OffsetStruct { \
name##Members(name, HEAP_OBJECT_OFFSET_MEMBER_EXPANSION) \
}; \
struct name##SizeStruct : base, name##OffsetStruct {}; \
struct name##Data { \
+ typedef base SuperClass; \
static Q_CONSTEXPR size_t baseOffset = sizeof(name##SizeStruct) - sizeof(name##OffsetStruct); \
name##Members(name, HEAP_OBJECT_MEMBER_EXPANSION) \
}; \
Q_STATIC_ASSERT(sizeof(name##SizeStruct) == sizeof(name##Data) + name##Data::baseOffset); \
-static Q_CONSTEXPR quint64 name##_markTable = \
- (name##Members(name##Data, HEAP_OBJECT_MARK_EXPANSION) 0) | QV4::Heap::base::markTable; \
- \
-struct name : base, name##Data
-#define DECLARE_MARK_TABLE(class) static Q_CONSTEXPR quint64 markTable = class##_markTable
+#define DECLARE_HEAP_OBJECT(name, base) \
+DECLARE_HEAP_OBJECT_BASE(name, base) \
+struct name : base, name##Data
+#define DECLARE_EXPORTED_HEAP_OBJECT(name, base) \
+DECLARE_HEAP_OBJECT_BASE(name, base) \
+struct Q_QML_EXPORT name : base, name##Data
+
+#define DECLARE_MARKOBJECTS(class) \
+ static void markObjects(Heap::Base *b, MarkStack *stack) { \
+ class *o = static_cast<class *>(b); \
+ class##Data::SuperClass::markObjects(o, stack); \
+ class##Members(class, HEAP_OBJECT_MARKOBJECTS_EXPANSION) \
+ }
}
diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h
index 86fd28000d..4ec224cc36 100644
--- a/src/qml/memory/qv4writebarrier_p.h
+++ b/src/qml/memory/qv4writebarrier_p.h
@@ -114,8 +114,8 @@ namespace Heap {
template <typename T, size_t o>
struct Pointer {
static Q_CONSTEXPR size_t offset = o;
- T operator->() const { return ptr; }
- operator T () const { return ptr; }
+ T operator->() const { return get(); }
+ operator T () const { return get(); }
Heap::Base *base() {
Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base));
@@ -124,14 +124,18 @@ struct Pointer {
}
void set(EngineBase *e, T newVal) {
- WriteBarrier::write(e, base(), reinterpret_cast<Heap::Base **>(&ptr), reinterpret_cast<Heap::Base *>(newVal));
+ WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal));
}
+ T get() const { return reinterpret_cast<T>(ptr); }
+
template <typename Type>
Type *cast() { return static_cast<Type *>(ptr); }
+ Heap::Base *heapObject() const { return ptr; }
+
private:
- T ptr;
+ Heap::Base *ptr;
};
typedef Pointer<char *, 0> V4PointerCheck;
V4_ASSERT_IS_TRIVIAL(V4PointerCheck)
@@ -192,6 +196,30 @@ struct ValueArray {
values[i] = values[i + n];
}
}
+
+ void mark(MarkStack *markStack) {
+ Value *v = values;
+ const Value *end = v + alloc;
+ if (alloc > 32*1024) {
+ // drain from time to time to avoid overflows in the js stack
+ Heap::Base **currentBase = markStack->top;
+ while (v < end) {
+ v->mark(markStack);
+ ++v;
+ if (markStack->top >= currentBase + 32*1024) {
+ Heap::Base **oldBase = markStack->base;
+ markStack->base = currentBase;
+ markStack->drain();
+ markStack->base = oldBase;
+ }
+ }
+ } else {
+ while (v < end) {
+ v->mark(markStack);
+ ++v;
+ }
+ }
+ }
};
// It's really important that the offset of values in this structure is
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index aa48accfe0..7291cf0d3d 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -100,7 +100,8 @@ enum Op {
Sub,
URShift,
InplaceURightShift,
- InplaceXor
+ InplaceXor,
+ Invalid
};
} // namespace QSOperator
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index e9d7dcbd2d..c13227d8fe 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -57,14 +57,16 @@ include(memory/memory.pri)
include(parser/parser.pri)
include(compiler/compiler.pri)
include(jsapi/jsapi.pri)
-include(jit/jit.pri)
include(jsruntime/jsruntime.pri)
+include(jit/jit.pri)
include(qml/qml.pri)
include(debugger/debugger.pri)
qtConfig(animation) {
include(animations/animations.pri)
}
include(types/types.pri)
+include(../3rdparty/masm/masm-defs.pri)
+include(../3rdparty/masm/masm.pri)
MODULE_PLUGIN_TYPES = \
qmltooling
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 43ab74138c..56ab259229 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -203,12 +203,11 @@ protected:
bool isUndefined = false;
- QV4::ScopedCallData callData(scope);
- QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
+ QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
bool error = false;
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
- error = !write(scope.result, isUndefined, flags);
+ error = !write(result, isUndefined, flags);
if (!watcher.wasDeleted()) {
@@ -457,12 +456,11 @@ QVariant QQmlBinding::evaluate()
bool isUndefined = false;
QV4::Scope scope(ep->v4engine());
- QV4::ScopedCallData callData(scope);
- QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
+ QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
ep->dereferenceScarceResources();
- return scope.engine->toVariant(scope.result, qMetaTypeId<QList<QObject*> >());
+ return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >());
}
QString QQmlBinding::expressionIdentifier() const
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index 19ece44beb..1d7a37fc99 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -55,6 +55,7 @@
#include <private/qjsvalue_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <QtCore/qdebug.h>
@@ -192,35 +193,35 @@ void QQmlBoundSignalExpression::evaluate(void **a)
int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, 0);
int argCount = argsTypes ? *argsTypes : 0;
- QV4::ScopedCallData callData(scope, argCount);
+ QV4::JSCallData jsCall(scope, argCount);
for (int ii = 0; ii < argCount; ++ii) {
int type = argsTypes[ii + 1];
//### ideally we would use metaTypeToJS, however it currently gives different results
// for several cases (such as QVariant type and QObject-derived types)
//args[ii] = engine->metaTypeToJS(type, a[ii + 1]);
if (type == qMetaTypeId<QJSValue>()) {
- if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &callData->args[ii]))
- callData->args[ii] = *v4Value;
+ if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &jsCall->args[ii]))
+ jsCall->args[ii] = *v4Value;
else
- callData->args[ii] = QV4::Encode::undefined();
+ jsCall->args[ii] = QV4::Encode::undefined();
} else if (type == QMetaType::QVariant) {
- callData->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1]));
+ jsCall->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1]));
} else if (type == QMetaType::Int) {
//### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
- callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
+ jsCall->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
} else if (type == qMetaTypeId<QQmlV4Handle>()) {
- callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]);
+ jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]);
} else if (ep->isQObject(type)) {
if (!*reinterpret_cast<void* const *>(a[ii + 1]))
- callData->args[ii] = QV4::Primitive::nullValue();
+ jsCall->args[ii] = QV4::Primitive::nullValue();
else
- callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1]));
+ jsCall->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1]));
} else {
- callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1]));
+ jsCall->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1]));
}
}
- QQmlJavaScriptExpression::evaluate(callData, 0, scope);
+ QQmlJavaScriptExpression::evaluate(jsCall.callData(), 0);
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
@@ -237,12 +238,12 @@ void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args)
ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
- QV4::ScopedCallData callData(scope, args.count());
+ QV4::JSCallData jsCall(scope, args.count());
for (int ii = 0; ii < args.count(); ++ii) {
- callData->args[ii] = scope.engine->fromVariant(args[ii]);
+ jsCall->args[ii] = scope.engine->fromVariant(args[ii]);
}
- QQmlJavaScriptExpression::evaluate(callData, 0, scope);
+ QQmlJavaScriptExpression::evaluate(jsCall.callData(), 0);
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 8ec8669c60..5a03f2dd93 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -58,6 +58,7 @@
#include <private/qv4scopedvalue_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4jscall_p.h>
#include <QDir>
#include <QStack>
@@ -1097,7 +1098,7 @@ namespace Heap {
Member(class, NoMark, QQmlQPointer<QObject>, parent)
DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) {
- DECLARE_MARK_TABLE(QmlIncubatorObject);
+ DECLARE_MARKOBJECTS(QmlIncubatorObject);
void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
inline void destroy();
@@ -1110,11 +1111,11 @@ struct QmlIncubatorObject : public QV4::Object
V4_OBJECT2(QmlIncubatorObject, Object)
V4_NEEDS_DESTROY
- static void method_get_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_set_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_status(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_get_statusChanged(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_set_statusChanged(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_get_status(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_get_object(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_forceCompletion(const BuiltinFunction *, CallData *callData);
void statusChanged(QQmlIncubator::Status);
void setInitialState(QObject *);
@@ -1224,8 +1225,7 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
if (engine->hasException)
return;
- QV4::ExecutionContextSaver saver(scope);
- engine->pushContext(qmlContext);
+ QV4::ScopedStackFrame frame(scope, qmlContext->d());
while (1) {
name = it.nextPropertyNameAsString(val);
@@ -1460,17 +1460,19 @@ QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4)
incubationProto.set(v4, proto);
}
-void QV4::QmlIncubatorObject::method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData)
+QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_object(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>());
if (!o)
THROW_TYPE_ERROR();
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object());
+ return QV4::QObjectWrapper::wrap(scope.engine, o->d()->incubator->object());
}
-void QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData)
+QV4::ReturnedValue QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>());
if (!o)
THROW_TYPE_ERROR();
@@ -1480,28 +1482,31 @@ void QV4::QmlIncubatorObject::method_forceCompletion(const BuiltinFunction *, Sc
RETURN_UNDEFINED();
}
-void QV4::QmlIncubatorObject::method_get_status(const BuiltinFunction *, Scope &scope, CallData *callData)
+QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_status(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>());
if (!o)
THROW_TYPE_ERROR();
- scope.result = QV4::Encode(o->d()->incubator->status());
+ return QV4::Encode(o->d()->incubator->status());
}
-void QV4::QmlIncubatorObject::method_get_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData)
+QV4::ReturnedValue QV4::QmlIncubatorObject::method_get_statusChanged(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>());
if (!o)
THROW_TYPE_ERROR();
- scope.result = o->d()->statusChanged;
+ return QV4::Encode(o->d()->statusChanged);
}
-void QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *, Scope &scope, CallData *callData)
+QV4::ReturnedValue QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QmlIncubatorObject> o(scope, callData->thisObject.as<QmlIncubatorObject>());
- if (!o || callData->argc < 1)
+ if (!o || callData->argc() < 1)
THROW_TYPE_ERROR();
o->d()->statusChanged.set(scope.engine, callData->args[0]);
@@ -1556,10 +1561,10 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
QV4::ScopedFunctionObject f(scope, d()->statusChanged);
if (f) {
- QV4::ScopedCallData callData(scope, 1);
- callData->thisObject = this;
- callData->args[0] = QV4::Primitive::fromUInt32(s);
- f->call(scope, callData);
+ QV4::JSCallData jsCallData(scope, 1);
+ *jsCallData->thisObject = this;
+ jsCallData->args[0] = QV4::Primitive::fromUInt32(s);
+ f->call(jsCallData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 13e62ec696..df4030e522 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -42,6 +42,7 @@
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <QQmlError>
@@ -63,17 +64,17 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en
QV4::Scope scope(engine);
QV4::ArrayObject *array = m_args.as<QV4::ArrayObject>();
+ const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>();
+ Q_ASSERT(callback);
const int argCount = array ? array->getLength() : 0;
- QV4::ScopedCallData callData(scope, argCount);
- callData->thisObject = QV4::Encode::undefined();
+ QV4::JSCallData jsCallData(scope, argCount);
+ *jsCallData->thisObject = QV4::Encode::undefined();
for (int i = 0; i < argCount; i++) {
- callData->args[i] = array->getIndexed(i);
+ jsCallData->args[i] = array->getIndexed(i);
}
- const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>();
- Q_ASSERT(callback);
- callback->call(scope, callData);
+ callback->call(jsCallData);
if (scope.engine->hasException) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
@@ -105,9 +106,10 @@ void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine)
m_tickedMethod = metaObject.method(methodIndex);
}
-void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- if (callData->argc == 0)
+ QV4::Scope scope(b);
+ if (callData->argc() == 0)
THROW_GENERIC_ERROR("Qt.callLater: no arguments given");
const QV4::FunctionObject *func = callData->args[0].as<QV4::FunctionObject>();
@@ -158,8 +160,8 @@ void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction
dfc.m_guarded = true;
} else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) {
QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope());
- Q_ASSERT(g->qml->scopeObject);
- dfc.m_objectGuard = QQmlGuard<QObject>(g->qml->scopeObject);
+ Q_ASSERT(g->qml()->scopeObject);
+ dfc.m_objectGuard = QQmlGuard<QObject>(g->qml()->scopeObject);
dfc.m_guarded = true;
}
}
@@ -169,22 +171,21 @@ void QQmlDelayedCallQueue::addUniquelyAndExecuteLater(const QV4::BuiltinFunction
m_tickedMethod.invoke(this, Qt::QueuedConnection);
m_callbackOutstanding = true;
}
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine)
{
- const int length = callData->argc - offset;
+ const int length = callData->argc() - offset;
if (length == 0) {
dfc.m_args.clear();
return;
}
QV4::Scope scope(engine);
QV4::ScopedArrayObject array(scope, engine->newArrayObject(length));
- int i = 0;
- for (int j = offset; j < callData->argc; ++i, ++j) {
+ uint i = 0;
+ for (int j = offset, ej = callData->argc(); j < ej; ++i, ++j)
array->putIndexed(i, callData->args[j]);
- }
dfc.m_args.set(engine, array);
}
diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h
index cffde4f0c0..5b3043cfed 100644
--- a/src/qml/qml/qqmldelayedcallqueue_p.h
+++ b/src/qml/qml/qqmldelayedcallqueue_p.h
@@ -70,7 +70,7 @@ public:
void init(QV4::ExecutionEngine *);
- void addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ QV4::ReturnedValue addUniquelyAndExecuteLater(const QV4::BuiltinFunction *, QV4::CallData *callData);
public Q_SLOTS:
void ticked();
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index 1e1fbcf448..35dbaccbbe 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -247,15 +247,14 @@ void QQmlExpression::setExpression(const QString &expression)
}
// Must be called with a valid handle scope
-void QQmlExpressionPrivate::v4value(bool *isUndefined, QV4::Scope &scope)
+QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined)
{
if (!expressionFunctionValid) {
createQmlBinding(context(), scopeObject(), expression, url, line);
expressionFunctionValid = true;
}
- QV4::ScopedCallData callData(scope);
- evaluate(callData, isUndefined, scope);
+ return evaluate(isUndefined);
}
QVariant QQmlExpressionPrivate::value(bool *isUndefined)
@@ -274,9 +273,9 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined)
{
QV4::Scope scope(QV8Engine::getV4(ep->v8engine()));
- v4value(isUndefined, scope);
+ QV4::ScopedValue result(scope, v4value(isUndefined));
if (!hasError())
- rv = scope.engine->toVariant(scope.result, -1);
+ rv = scope.engine->toVariant(result, -1);
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h
index 44342a957b..a94ca0fc2d 100644
--- a/src/qml/qml/qqmlexpression_p.h
+++ b/src/qml/qml/qqmlexpression_p.h
@@ -75,7 +75,7 @@ public:
QVariant value(bool *isUndefined = 0);
- void v4value(bool *isUndefined, QV4::Scope &scope);
+ QV4::ReturnedValue v4value(bool *isUndefined = 0);
static inline QQmlExpressionPrivate *get(QQmlExpression *expr);
static inline QQmlExpression *get(QQmlExpressionPrivate *expr);
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 31c277d283..006611e089 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -46,6 +46,7 @@
#include <private/qv4script_p.h>
#include <private/qv4errorobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
@@ -180,9 +181,16 @@ void QQmlJavaScriptExpression::refresh()
{
}
+QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined)
+{
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine);
+ QV4::Scope scope(v4);
+ QV4::JSCallData jsCall(scope);
+ return evaluate(jsCall.callData(), isUndefined);
+}
-void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope)
+QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined)
{
Q_ASSERT(m_context && m_context->engine);
@@ -190,7 +198,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin
if (!v4Function) {
if (isUndefined)
*isUndefined = true;
- return;
+ return QV4::Encode::undefined();
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine);
@@ -210,19 +218,20 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin
capture.guards.copyAndClearPrepend(activeGuards);
QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine());
- scope.result = QV4::Primitive::undefinedValue();
callData->thisObject = v4->globalObject;
if (scopeObject()) {
- QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject()));
- if (value->isObject())
- callData->thisObject = value;
+ QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject());
+ if (QV4::Value::fromReturnedValue(scope).isObject())
+ callData->thisObject = scope;
}
- QV4::ExecutionContext *outer = static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef());
- if (v4Function->canUseSimpleFunction()) {
- outer->simpleCall(scope, callData, v4Function);
- } else {
- outer->call(scope, callData, v4Function);
+ Q_ASSERT(m_qmlScope.valueRef());
+ QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
+ QV4::Scope scope(v4);
+ QV4::ScopedValue result(scope, res);
+ if (v4Function->hasQmlDependencies) {
+ QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d();
+ QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction);
}
if (scope.hasException()) {
@@ -234,7 +243,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin
*isUndefined = true;
} else {
if (isUndefined)
- *isUndefined = scope.result.isUndefined();
+ *isUndefined = result->isUndefined();
if (!watcher.wasDeleted() && hasDelayedError())
delayedError()->clearError();
@@ -251,6 +260,8 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin
g->Delete();
ep->propertyCapture = lastPropertyCapture;
+
+ return result->asReturnedValue();
}
void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
@@ -329,12 +340,11 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur
}
}
-void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope)
+void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction)
{
// Let the caller check and avoid the function call :)
Q_ASSERT(compiledFunction->hasQmlDependencies());
- QV4::ExecutionEngine *engine = scope.engine;
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
if (!ep)
return;
@@ -347,8 +357,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct
capture->expression->m_permanentDependenciesRegistered = true;
- QV4::Scoped<QV4::QmlContext> context(scope, engine->qmlContext());
- QQmlContextData *qmlContext = context->qmlContext();
+ QV4::Heap::QQmlContextWrapper *wrapper = context->qml();
+ QQmlContextData *qmlContext = wrapper->context->contextData();
const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
const int idObjectDependencyCount = compiledFunction->nDependingIdObjects;
@@ -368,7 +378,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct
QQmlPropertyCapture::Permanently);
}
- QObject *scopeObject = context->qmlScope();
+ QObject *scopeObject = wrapper->scopeObject;
const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties;
for (int i = 0; i < scopePropertyDependencyCount; ++i) {
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index e9a9f4feee..1cb6d7bfd1 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -103,7 +103,8 @@ public:
virtual QString expressionIdentifier() const = 0;
virtual void expressionChanged() = 0;
- void evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope);
+ QV4::ReturnedValue evaluate(bool *isUndefined);
+ QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined);
inline bool notifyOnValueChanged() const;
@@ -204,7 +205,7 @@ public:
Permanently
};
- static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope);
+ static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction);
void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce);
void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true);
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 43677e0d78..b4be83a156 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -171,8 +171,9 @@ void PropertyListPrototype::init(ExecutionEngine *)
defineDefaultProperty(QStringLiteral("push"), method_push, 1);
}
-void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue PropertyListPrototype::method_push(const BuiltinFunction *b, CallData *callData)
{
+ Scope scope(b);
ScopedObject instance(scope, callData->thisObject.toObject(scope.engine));
if (!instance)
RETURN_UNDEFINED();
@@ -183,12 +184,13 @@ void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, C
THROW_GENERIC_ERROR("List doesn't define an Append function");
QV4::ScopedObject so(scope);
- for (int i = 0; i < callData->argc; ++i)
+ for (int i = 0, ei = callData->argc(); i < ei; ++i)
{
so = callData->args[i].toObject(scope.engine);
if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>())
w->d()->property().append(&w->d()->property(), wrapper->object() );
}
+ return Encode::undefined();
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 84dadba01a..0b53395d2b 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -103,7 +103,7 @@ struct PropertyListPrototype : Object
{
void init(ExecutionEngine *engine);
- static void method_push(const BuiltinFunction *, Scope &, CallData *callData);
+ static ReturnedValue method_push(const BuiltinFunction *, CallData *callData);
};
}
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 326b36c5cd..3f2a373966 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -58,8 +58,7 @@ DEFINE_OBJECT_VTABLE(QQmlLocaleData);
#define THROW_ERROR(string) \
do { \
- scope.result = scope.engine->throwError(QString::fromUtf8(string)); \
- return; \
+ return scope.engine->throwError(QString::fromUtf8(string)); \
} while (false)
@@ -87,37 +86,32 @@ void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine)
engine->dateCtor()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated);
}
-void QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc > 2) {
- QV4::DatePrototype::method_toLocaleString(b, scope, callData);
- return;
- }
+ Scope scope(b);
+ if (callData->argc() > 2)
+ return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc());
QV4::DateObject *date = callData->thisObject.as<DateObject>();
- if (!date) {
- QV4::DatePrototype::method_toLocaleString(b, scope, callData);
- return;
- }
+ if (!date)
+ return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc());
QDateTime dt = date->toQDateTime();
- if (callData->argc == 0) {
+ if (callData->argc() == 0) {
// Use QLocale for standard toLocaleString() function
QLocale locale;
RETURN_RESULT(scope.engine->newString(locale.toString(dt)));
}
- if (!isLocaleObject(callData->args[0])) {
- QV4::DatePrototype::method_toLocaleString(b, scope, callData); // Use the default Date toLocaleString()
- return;
- }
+ if (!isLocaleObject(callData->args[0]))
+ return QV4::DatePrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleString()
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
QLocale::FormatType enumFormat = QLocale::LongFormat;
QString formattedDt;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
if (String *s = callData->args[1].stringValue()) {
QString format = s->toQString();
formattedDt = r->d()->locale->toString(dt, format);
@@ -132,39 +126,36 @@ void QQmlDateExtension::method_toLocaleString(const BuiltinFunction *b, Scope &s
formattedDt = r->d()->locale->toString(dt, enumFormat);
}
- scope.result = scope.engine->newString(formattedDt);
+ RETURN_RESULT(scope.engine->newString(formattedDt));
}
-void QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc > 2) {
- QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData);
- return;
- }
+ Scope scope(b);
+ if (callData->argc() > 2)
+ return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc());
QV4::DateObject *date = callData->thisObject.as<DateObject>();
- if (!date) {
- QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData);
- return;
- }
+ if (!date)
+ return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc());
QDateTime dt = date->toQDateTime();
QTime time = dt.time();
- if (callData->argc == 0) {
+ if (callData->argc() == 0) {
// Use QLocale for standard toLocaleString() function
QLocale locale;
RETURN_RESULT(scope.engine->newString(locale.toString(time)));
}
if (!isLocaleObject(callData->args[0]))
- return QV4::DatePrototype::method_toLocaleTimeString(b, scope, callData); // Use the default Date toLocaleTimeString()
+ return QV4::DatePrototype::method_toLocaleTimeString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleTimeString()
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
QLocale::FormatType enumFormat = QLocale::LongFormat;
QString formattedTime;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
if (String *s = callData->args[1].stringValue()) {
QString format = s->toQString();
formattedTime = r->d()->locale->toString(time, format);
@@ -179,39 +170,36 @@ void QQmlDateExtension::method_toLocaleTimeString(const BuiltinFunction *b, Scop
formattedTime = r->d()->locale->toString(time, enumFormat);
}
- scope.result = scope.engine->newString(formattedTime);
+ RETURN_RESULT(scope.engine->newString(formattedTime));
}
-void QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc > 2) {
- QV4::DatePrototype::method_toLocaleDateString(b, scope, callData);
- return;
- }
+ Scope scope(b);
+ if (callData->argc() > 2)
+ return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc());
QV4::DateObject *dateObj = callData->thisObject.as<DateObject>();
- if (!dateObj) {
- QV4::DatePrototype::method_toLocaleDateString(b, scope, callData);
- return;
- }
+ if (!dateObj)
+ return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc());
QDateTime dt = dateObj->toQDateTime();
QDate date = dt.date();
- if (callData->argc == 0) {
+ if (callData->argc() == 0) {
// Use QLocale for standard toLocaleString() function
QLocale locale;
RETURN_RESULT(scope.engine->newString(locale.toString(date)));
}
if (!isLocaleObject(callData->args[0]))
- return QV4::DatePrototype::method_toLocaleDateString(b, scope, callData); // Use the default Date toLocaleDateString()
+ return QV4::DatePrototype::method_toLocaleDateString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Date toLocaleDateString()
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
QLocale::FormatType enumFormat = QLocale::LongFormat;
QString formattedDate;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
if (String *s = callData->args[1].stringValue()) {
QString format = s->toQString();
formattedDate = r->d()->locale->toString(date, format);
@@ -226,13 +214,14 @@ void QQmlDateExtension::method_toLocaleDateString(const BuiltinFunction *b, Scop
formattedDate = r->d()->locale->toString(date, enumFormat);
}
- scope.result = scope.engine->newString(formattedDate);
+ RETURN_RESULT(scope.engine->newString(formattedDate));
}
-void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::ExecutionEngine * const engine = scope.engine;
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
if (String *s = callData->args[0].stringValue()) {
QLocale locale;
QString dateString = s->toQString();
@@ -241,7 +230,7 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &
}
}
- if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0]))
+ if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0]))
THROW_ERROR("Locale: Date.fromLocaleString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
@@ -249,7 +238,7 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &
QLocale::FormatType enumFormat = QLocale::LongFormat;
QDateTime dt;
QString dateString = callData->args[1].toQStringNoThrow();
- if (callData->argc == 3) {
+ if (callData->argc() == 3) {
if (String *s = callData->args[2].stringValue()) {
QString format = s->toQString();
dt = r->d()->locale->toDateTime(dateString, format);
@@ -264,14 +253,15 @@ void QQmlDateExtension::method_fromLocaleString(const BuiltinFunction *, Scope &
dt = r->d()->locale->toDateTime(dateString, enumFormat);
}
- scope.result = engine->newDateObject(dt);
+ RETURN_RESULT(engine->newDateObject(dt));
}
-void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::ExecutionEngine * const engine = scope.engine;
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
if (String *s = callData->args[0].stringValue()) {
QLocale locale;
QString timeString = s->toQString();
@@ -282,7 +272,7 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco
}
}
- if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0]))
+ if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0]))
THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
@@ -290,7 +280,7 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco
QLocale::FormatType enumFormat = QLocale::LongFormat;
QTime tm;
QString dateString = callData->args[1].toQStringNoThrow();
- if (callData->argc == 3) {
+ if (callData->argc() == 3) {
if (String *s = callData->args[2].stringValue()) {
QString format = s->toQString();
tm = r->d()->locale->toTime(dateString, format);
@@ -314,11 +304,12 @@ void QQmlDateExtension::method_fromLocaleTimeString(const BuiltinFunction *, Sco
RETURN_RESULT(engine->newDateObject(dt));
}
-void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QV4::ExecutionEngine * const engine = scope.engine;
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
if (String *s = callData->args[0].stringValue()) {
QLocale locale;
QString dateString = s->toQString();
@@ -327,7 +318,7 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco
}
}
- if (callData->argc < 1 || callData->argc > 3 || !isLocaleObject(callData->args[0]))
+ if (callData->argc() < 1 || callData->argc() > 3 || !isLocaleObject(callData->args[0]))
THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
@@ -335,7 +326,7 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco
QLocale::FormatType enumFormat = QLocale::LongFormat;
QDate dt;
QString dateString = callData->args[1].toQStringNoThrow();
- if (callData->argc == 3) {
+ if (callData->argc() == 3) {
if (String *s = callData->args[2].stringValue()) {
QString format = s->toQString();
dt = r->d()->locale->toDate(dateString, format);
@@ -353,9 +344,10 @@ void QQmlDateExtension::method_fromLocaleDateString(const BuiltinFunction *, Sco
RETURN_RESULT(engine->newDateObject(QDateTime(dt)));
}
-void QQmlDateExtension::method_timeZoneUpdated(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 0)
+ QV4::Scope scope(b);
+ if (callData->argc() != 0)
THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments");
QV4::DatePrototype::timezoneUpdated();
@@ -373,28 +365,27 @@ void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine)
engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString);
}
-void QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, Scope &scope, CallData *callData)
+QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc > 3)
+ QV4::Scope scope(b);
+ if (callData->argc() > 3)
THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments");
double number = callData->thisObject.toNumber();
- if (callData->argc == 0) {
+ if (callData->argc() == 0) {
// Use QLocale for standard toLocaleString() function
QLocale locale;
RETURN_RESULT(scope.engine->newString(locale.toString(number)));
}
- if (!isLocaleObject(callData->args[0])) {
- QV4::NumberPrototype::method_toLocaleString(b, scope, callData); // Use the default Number toLocaleString()
- return;
- }
+ if (!isLocaleObject(callData->args[0]))
+ return QV4::NumberPrototype::method_toLocaleString(b, &callData->thisObject, callData->args, callData->argc()); // Use the default Number toLocaleString()
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
quint16 format = 'f';
- if (callData->argc > 1) {
+ if (callData->argc() > 1) {
if (!callData->args[1].isString())
THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments");
QString fs = callData->args[1].toQString();
@@ -402,23 +393,24 @@ void QQmlNumberExtension::method_toLocaleString(const BuiltinFunction *b, Scope
format = fs.at(0).unicode();
}
int prec = 2;
- if (callData->argc > 2) {
+ if (callData->argc() > 2) {
if (!callData->args[2].isNumber())
THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments");
prec = callData->args[2].toInt32();
}
- scope.result = scope.engine->newString(r->d()->locale->toString(number, (char)format, prec));
+ RETURN_RESULT(scope.engine->newString(r->d()->locale->toString(number, (char)format, prec)));
}
-void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc > 2)
+ QV4::Scope scope(b);
+ if (callData->argc() > 2)
THROW_ERROR("Locale: Number.toLocaleCurrencyString(): Invalid arguments");
double number = callData->thisObject.toNumber();
- if (callData->argc == 0) {
+ if (callData->argc() == 0) {
// Use QLocale for standard toLocaleString() function
QLocale locale;
RETURN_RESULT(scope.engine->newString(locale.toString(number)));
@@ -430,7 +422,7 @@ void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *,
GET_LOCALE_DATA_RESOURCE(callData->args[0]);
QString symbol;
- if (callData->argc > 1) {
+ if (callData->argc() > 1) {
if (!callData->args[1].isString())
THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments");
symbol = callData->args[1].toQStringNoThrow();
@@ -439,15 +431,16 @@ void QQmlNumberExtension::method_toLocaleCurrencyString(const BuiltinFunction *,
RETURN_RESULT(scope.engine->newString(r->d()->locale->toCurrencyString(number, symbol)));
}
-void QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1 || callData->argc > 2)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1 || callData->argc() > 2)
THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments");
int numberIdx = 0;
QLocale locale;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
if (!isLocaleObject(callData->args[0]))
THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments");
@@ -467,45 +460,49 @@ void QQmlNumberExtension::method_fromLocaleString(const BuiltinFunction *, Scope
if (!ok)
THROW_ERROR("Locale: Number.fromLocaleString(): Invalid format");
- scope.result = QV4::Encode(val);
+ RETURN_RESULT(QV4::Encode(val));
}
//--------------
// Locale object
-void QQmlLocaleData::method_get_firstDayOfWeek(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_get_firstDayOfWeek(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
+ return Encode::undefined();
int fdow = int(locale->firstDayOfWeek());
if (fdow == 7)
fdow = 0; // Qt::Sunday = 7, but Sunday is 0 in JS Date
- scope.result = QV4::Encode(fdow);
+ RETURN_RESULT(fdow);
}
-void QQmlLocaleData::method_get_measurementSystem(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_get_measurementSystem(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
- scope.result = QV4::Encode(locale->measurementSystem());
+ return Encode::undefined();
+ return QV4::Encode(locale->measurementSystem());
}
-void QQmlLocaleData::method_get_textDirection(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_get_textDirection(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
+ return Encode::undefined();
- scope.result = QV4::Encode(locale->textDirection());
+ return QV4::Encode(locale->textDirection());
}
-void QQmlLocaleData::method_get_weekDays(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_get_weekDays(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
+ return Encode::undefined();
QList<Qt::DayOfWeek> days = locale->weekdays();
@@ -519,14 +516,15 @@ void QQmlLocaleData::method_get_weekDays(const BuiltinFunction *, Scope &scope,
}
result->setArrayLengthUnchecked(days.size());
- scope.result = result.asReturnedValue();
+ return result.asReturnedValue();
}
-void QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
+ return Encode::undefined();
QStringList langs = locale->uiLanguages();
QV4::ScopedArrayObject result(scope, scope.engine->newArrayObject());
@@ -537,40 +535,42 @@ void QQmlLocaleData::method_get_uiLanguages(const BuiltinFunction *, Scope &scop
result->setArrayLengthUnchecked(langs.size());
- scope.result = result.asReturnedValue();
+ return result.asReturnedValue();
}
-void QQmlLocaleData::method_currencySymbol(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocaleData::method_currencySymbol(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QLocale *locale = getThisLocale(scope, callData);
if (!locale)
- return;
+ return Encode::undefined();
- if (callData->argc > 1)
+ if (callData->argc() > 1)
THROW_ERROR("Locale: currencySymbol(): Invalid arguments");
QLocale::CurrencySymbolFormat format = QLocale::CurrencySymbol;
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
quint32 intFormat = callData->args[0].toNumber();
format = QLocale::CurrencySymbolFormat(intFormat);
}
- scope.result = scope.engine->newString(locale->currencySymbol(format));
+ RETURN_RESULT(scope.engine->newString(locale->currencySymbol(format)));
}
#define LOCALE_FORMAT(FUNC) \
-void QQmlLocaleData::method_ ##FUNC (const BuiltinFunction *, Scope &scope, CallData *callData) { \
+ReturnedValue QQmlLocaleData::method_ ##FUNC (const BuiltinFunction *b, CallData *callData) { \
+ QV4::Scope scope(b); \
QLocale *locale = getThisLocale(scope, callData); \
if (!locale) \
- return; \
- if (callData->argc > 1) \
+ return Encode::undefined(); \
+ if (callData->argc() > 1) \
THROW_ERROR("Locale: " #FUNC "(): Invalid arguments"); \
QLocale::FormatType format = QLocale::LongFormat;\
- if (callData->argc == 1) { \
+ if (callData->argc() == 1) { \
quint32 intFormat = callData->args[0].toUInt32(); \
format = QLocale::FormatType(intFormat); \
} \
- scope.result = scope.engine->newString(locale-> FUNC (format)); \
+ RETURN_RESULT(scope.engine->newString(locale-> FUNC (format))); \
}
LOCALE_FORMAT(dateTimeFormat)
@@ -579,18 +579,19 @@ LOCALE_FORMAT(dateFormat)
// +1 added to idx because JS is 0-based, whereas QLocale months begin at 1.
#define LOCALE_FORMATTED_MONTHNAME(VARIABLE) \
-void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\
+ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) {\
+ Scope scope(b); \
QLocale *locale = getThisLocale(scope, callData); \
if (!locale) \
- return; \
- if (callData->argc < 1 || callData->argc > 2) \
+ return Encode::undefined(); \
+ if (callData->argc() < 1 || callData->argc() > 2) \
THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
QLocale::FormatType enumFormat = QLocale::LongFormat; \
int idx = callData->args[0].toInt32() + 1; \
if (idx < 1 || idx > 12) \
THROW_ERROR("Locale: Invalid month"); \
QString name; \
- if (callData->argc == 2) { \
+ if (callData->argc() == 2) { \
if (callData->args[1].isNumber()) { \
quint32 intFormat = callData->args[1].toUInt32(); \
QLocale::FormatType format = QLocale::FormatType(intFormat); \
@@ -601,16 +602,17 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope,
} else { \
name = locale-> VARIABLE(idx, enumFormat); \
} \
- scope.result = scope.engine->newString(name); \
+ RETURN_RESULT(scope.engine->newString(name)); \
}
// 0 -> 7 as Qt::Sunday is 7, but Sunday is 0 in JS Date
#define LOCALE_FORMATTED_DAYNAME(VARIABLE) \
-void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) {\
+ReturnedValue QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) {\
+ Scope scope(b); \
QLocale *locale = getThisLocale(scope, callData); \
if (!locale) \
- return; \
- if (callData->argc < 1 || callData->argc > 2) \
+ return Encode::undefined(); \
+ if (callData->argc() < 1 || callData->argc() > 2) \
THROW_ERROR("Locale: " #VARIABLE "(): Invalid arguments"); \
QLocale::FormatType enumFormat = QLocale::LongFormat; \
int idx = callData->args[0].toInt32(); \
@@ -618,7 +620,7 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope,
THROW_ERROR("Locale: Invalid day"); \
if (idx == 0) idx = 7; \
QString name; \
- if (callData->argc == 2) { \
+ if (callData->argc() == 2) { \
if (callData->args[1].isNumber()) { \
quint32 intFormat = callData->args[1].toUInt32(); \
QLocale::FormatType format = QLocale::FormatType(intFormat); \
@@ -629,7 +631,7 @@ void QQmlLocaleData::method_ ## VARIABLE (const BuiltinFunction *, Scope &scope,
} else { \
name = locale-> VARIABLE(idx, enumFormat); \
} \
- scope.result = scope.engine->newString(name); \
+ RETURN_RESULT(scope.engine->newString(name)); \
}
LOCALE_FORMATTED_MONTHNAME(monthName)
@@ -637,12 +639,14 @@ LOCALE_FORMATTED_MONTHNAME(standaloneMonthName)
LOCALE_FORMATTED_DAYNAME(dayName)
LOCALE_FORMATTED_DAYNAME(standaloneDayName)
-#define LOCALE_STRING_PROPERTY(VARIABLE) void QQmlLocaleData::method_get_ ## VARIABLE (const BuiltinFunction *, Scope &scope, CallData *callData) \
+#define LOCALE_STRING_PROPERTY(VARIABLE) \
+ReturnedValue QQmlLocaleData::method_get_ ## VARIABLE (const BuiltinFunction *b, CallData *callData) \
{ \
+ Scope scope(b); \
QLocale *locale = getThisLocale(scope, callData); \
if (!locale) \
- return; \
- scope.result = scope.engine->newString(locale-> VARIABLE());\
+ return Encode::undefined(); \
+ RETURN_RESULT(scope.engine->newString(locale-> VARIABLE()));\
}
LOCALE_STRING_PROPERTY(name)
@@ -833,22 +837,18 @@ void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine)
engine->stringPrototype()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare);
}
-void QQmlLocale::method_localeCompare(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QQmlLocale::method_localeCompare(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1 || (!callData->args[0].isString() && !callData->args[0].as<StringObject>())) {
- QV4::StringPrototype::method_localeCompare(b, scope, callData);
- return;
- }
+ if (callData->argc() != 1 || (!callData->args[0].isString() && !callData->args[0].as<StringObject>()))
+ return QV4::StringPrototype::method_localeCompare(b, &callData->thisObject, callData->args, callData->argc());
- if (!callData->thisObject.isString() && !callData->thisObject.as<StringObject>()) {
- QV4::StringPrototype::method_localeCompare(b, scope, callData);
- return;
- }
+ if (!callData->thisObject.isString() && !callData->thisObject.as<StringObject>())
+ return QV4::StringPrototype::method_localeCompare(b, &callData->thisObject, callData->args, callData->argc());
QString thisString = callData->thisObject.toQStringNoThrow();
QString thatString = callData->args[0].toQStringNoThrow();
- scope.result = QV4::Encode(QString::localeAwareCompare(thisString, thatString));
+ return QV4::Encode(QString::localeAwareCompare(thisString, thatString));
}
/*!
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 1a2ffc72b0..a81fe07b8e 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -67,13 +67,13 @@ public:
static void registerExtension(QV4::ExecutionEngine *engine);
private:
- static void method_toLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_toLocaleTimeString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_toLocaleDateString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fromLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fromLocaleTimeString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fromLocaleDateString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_timeZoneUpdated(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_toLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_toLocaleTimeString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_toLocaleDateString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fromLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fromLocaleTimeString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fromLocaleDateString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_timeZoneUpdated(const QV4::BuiltinFunction *, QV4::CallData *callData);
};
@@ -83,9 +83,9 @@ public:
static void registerExtension(QV4::ExecutionEngine *engine);
private:
- static void method_toLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fromLocaleString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_toLocaleCurrencyString(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_toLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fromLocaleString(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_toLocaleCurrencyString(const QV4::BuiltinFunction *, QV4::CallData *callData);
};
@@ -135,7 +135,7 @@ public:
private:
QQmlLocale();
- static void method_localeCompare(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_localeCompare(const QV4::BuiltinFunction *, QV4::CallData *callData);
};
namespace QV4 {
@@ -168,33 +168,33 @@ struct QQmlLocaleData : public QV4::Object
return thisObject->d()->locale;
}
- static void method_currencySymbol(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_dateTimeFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_timeFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_dateFormat(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_monthName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_standaloneMonthName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_dayName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_standaloneDayName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_firstDayOfWeek(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_measurementSystem(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_textDirection(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_weekDays(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_uiLanguages(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_nativeLanguageName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_nativeCountryName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_decimalPoint(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_groupSeparator(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_percent(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_zeroDigit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_negativeSign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_positiveSign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_exponential(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_amText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_pmText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_currencySymbol(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_dateTimeFormat(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_timeFormat(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_dateFormat(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_monthName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_standaloneMonthName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_dayName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_standaloneDayName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+
+ static QV4::ReturnedValue method_get_firstDayOfWeek(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_measurementSystem(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_textDirection(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_weekDays(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_uiLanguages(const QV4::BuiltinFunction *, QV4::CallData *callData);
+
+ static QV4::ReturnedValue method_get_name(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_nativeLanguageName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_nativeCountryName(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_decimalPoint(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_groupSeparator(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_percent(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_zeroDigit(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_negativeSign(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_positiveSign(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_exponential(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_amText(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_pmText(const QV4::BuiltinFunction *, QV4::CallData *callData);
};
}
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 4f69017ff0..5836c85666 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -260,8 +260,6 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
{
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle());
- v4->pushGlobalContext();
if (scriptCallback && scriptApi(e).isUndefined()) {
setScriptApi(e, scriptCallback(e, e));
} else if (qobjectCallback && !qobjectApi(e)) {
@@ -277,7 +275,6 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
QObject *o = component.create();
setQObjectApi(e, o);
}
- v4->popContext();
}
void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e)
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 494b3d30bc..55e05afdaa 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -44,6 +44,7 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlthread_p.h>
+#include <private/qv4codegen_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlprofiler_p.h>
#include <private/qqmlmemoryprofiler_p.h>
@@ -2080,10 +2081,10 @@ bool QQmlTypeData::tryLoadFromDiskCache()
if (!v4)
return false;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
{
QString error;
- if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), v4->iselFactory.data(), &error)) {
+ if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error;
return false;
}
@@ -2447,7 +2448,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit>
m_document.reset(new QmlIR::Document(isDebugging()));
QmlIR::IRLoader loader(unit->data, m_document.data());
loader.load();
- m_document->jsModule.setFileName(finalUrlString());
+ m_document->jsModule.fileName = finalUrlString();
m_document->javaScriptCompilationUnit = unit;
continueLoadFromIR();
}
@@ -2564,7 +2565,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
QString errorString;
if (m_compiledData->saveToDisk(url(), &errorString)) {
QString error;
- if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), enginePrivate->v4engine()->iselFactory.data(), &error)) {
+ if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
// ignore error, keep using the in-memory compilation unit.
}
} else {
@@ -2881,7 +2882,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
ep->warning(error);
}
- QV4::ScopedValue retval(scope, qmlContext->d()->qml);
+ QV4::ScopedValue retval(scope, qmlContext->d()->qml());
if (shared) {
m_value.set(scope.engine, retval);
m_loaded = true;
@@ -2923,19 +2924,12 @@ QQmlScriptData *QQmlScriptBlob::scriptData() const
return m_scriptData;
}
-struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit
-{
- void linkBackendToEngine(QV4::ExecutionEngine *) override {}
-};
-
void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
{
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
-
if (!disableDiskCache() || forceDiskCache()) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
QString error;
- if (unit->loadFromDisk(url(), data.sourceTimeStamp(), v4->iselFactory.data(), &error)) {
+ if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
initializeFromCompilationUnit(unit);
return;
} else {
@@ -2957,7 +2951,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator);
QList<QQmlError> errors;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, v4, finalUrl(), source, &errors, &collector);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, finalUrl(), source, &errors, &collector);
// No need to addref on unit, it's initial refcount is 1
source.clear();
if (!errors.isEmpty()) {
@@ -2965,7 +2959,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
if (!unit) {
- unit.adopt(new EmptyCompilationUnit);
+ unit.adopt(new QV4::CompiledData::CompilationUnit);
}
irUnit.javaScriptCompilationUnit = unit;
irUnit.imports = collector.imports;
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index bb65093163..25ff7ba7c8 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -60,6 +60,9 @@
QT_BEGIN_NAMESPACE
class QQmlTypeNameCache;
+class QQmlType;
+class QQmlTypePrivate;
+struct QQmlImportRef;
namespace QV4 {
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index a5cdccc97a..90ca08537c 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -316,8 +316,9 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
return true;
}
-void QQmlValueTypeWrapper::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QQmlValueTypeWrapper::method_toString(const BuiltinFunction *b, CallData *callData)
{
+ Scope scope(b);
Object *o = callData->thisObject.as<Object>();
if (!o)
THROW_TYPE_ERROR();
@@ -350,7 +351,7 @@ void QQmlValueTypeWrapper::method_toString(const BuiltinFunction *, Scope &scope
}
result += QLatin1Char(')');
}
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty)
@@ -472,13 +473,13 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) {
Q_ASSERT(!binding->isValueTypeProxy());
const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
- const auto stackFrame = v4->currentStackFrame();
+ const auto stackFrame = v4->currentStackFrame;
qCInfo(lcBindingRemoval,
"Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(),
qPrintable(qmlBinding->expressionIdentifier()),
metaObject->property(pd->coreIndex()).name(),
- qPrintable(stackFrame.source), stackFrame.line);
+ qPrintable(stackFrame->source()), stackFrame->lineNumber());
}
}
QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()));
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index c8aac719ab..da03af6dbc 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -56,6 +56,7 @@
#include <private/qv4value_p.h>
#include <private/qv4object_p.h>
+#include <private/qqmlpropertycache_p.h>
QT_BEGIN_NAMESPACE
@@ -111,7 +112,7 @@ public:
static PropertyAttributes query(const Managed *, String *name);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData);
static void initProto(ExecutionEngine *v4);
};
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index dde8784eb1..281d64ac79 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -55,6 +55,7 @@
#include <private/qv4variantobject_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
@@ -949,20 +950,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
const unsigned int parameterCount = function->formalParameterCount();
- QV4::ScopedCallData callData(scope, parameterCount);
- callData->thisObject = ep->v8engine()->global();
+ QV4::JSCallData jsCallData(scope, parameterCount);
+ *jsCallData->thisObject = ep->v8engine()->global();
for (uint ii = 0; ii < parameterCount; ++ii)
- callData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]);
+ jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]);
- function->call(scope, callData);
+ QV4::ScopedValue result(scope, function->call(jsCallData));
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
if (error.isValid())
ep->warning(error);
if (a[0]) *(QVariant *)a[0] = QVariant();
} else {
- if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(scope.result, 0);
+ if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0);
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 08f3d35e46..08842e714c 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -51,6 +51,7 @@
#include <private/qv4engine_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <QtCore/qobject.h>
#include <QtQml/qjsvalue.h>
@@ -74,8 +75,7 @@ using namespace QV4;
#define V4THROW_REFERENCE(string) \
do { \
ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \
- scope.result = scope.engine->throwError(error); \
- return; \
+ return scope.engine->throwError(error); \
} while (false)
QT_BEGIN_NAMESPACE
@@ -276,25 +276,25 @@ public:
static void initClass(ExecutionEngine *engine);
// JS API
- static void method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_nodeType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_namespaceUri(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_parentNode(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_childNodes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_firstChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_lastChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_previousSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_attributes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- //static void ownerDocument(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- //static void namespaceURI(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- //static void prefix(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- //static void localName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- //static void baseURI(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- //static void textContent(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_get_nodeName(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_nodeValue(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_nodeType(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_namespaceUri(const BuiltinFunction *b, QV4::CallData *callData);
+
+ static ReturnedValue method_get_parentNode(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_childNodes(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_firstChild(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_lastChild(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_previousSibling(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_nextSibling(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_attributes(const BuiltinFunction *b, QV4::CallData *callData);
+
+ //static ReturnedValue ownerDocument(const BuiltinFunction *b, QV4::CallData *callData);
+ //static ReturnedValue namespaceURI(const BuiltinFunction *b, QV4::CallData *callData);
+ //static ReturnedValue prefix(const BuiltinFunction *b, QV4::CallData *callData);
+ //static ReturnedValue localName(const BuiltinFunction *b, QV4::CallData *callData);
+ //static ReturnedValue baseURI(const BuiltinFunction *b, QV4::CallData *callData);
+ //static ReturnedValue textContent(const BuiltinFunction *b, QV4::CallData *callData);
static ReturnedValue getProto(ExecutionEngine *v4);
@@ -355,10 +355,10 @@ class Attr : public Node
{
public:
// JS API
- static void method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_name(const BuiltinFunction *b, QV4::CallData *callData);
// static void specified(CallContext *);
- static void method_value(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_ownerElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_value(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_ownerElement(const BuiltinFunction *b, QV4::CallData *callData);
// static void schemaTypeInfo(CallContext *);
// static void isId(CallContext *c);
@@ -370,7 +370,7 @@ class CharacterData : public Node
{
public:
// JS API
- static void method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_length(const BuiltinFunction *b, QV4::CallData *callData);
// C++ API
static ReturnedValue prototype(ExecutionEngine *v4);
@@ -380,8 +380,8 @@ class Text : public CharacterData
{
public:
// JS API
- static void method_isElementContentWhitespace(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_wholeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_isElementContentWhitespace(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_wholeText(const BuiltinFunction *b, QV4::CallData *callData);
// C++ API
static ReturnedValue prototype(ExecutionEngine *);
@@ -398,10 +398,10 @@ class Document : public Node
{
public:
// JS API
- static void method_xmlVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_xmlEncoding(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_xmlStandalone(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_documentElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_xmlVersion(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_xmlEncoding(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_xmlStandalone(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_documentElement(const BuiltinFunction *b, QV4::CallData *callData);
// C++ API
static ReturnedValue prototype(ExecutionEngine *);
@@ -420,8 +420,9 @@ void NodeImpl::release()
document->release();
}
-void NodePrototype::method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_nodeName(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
@@ -441,11 +442,12 @@ void NodePrototype::method_get_nodeName(const QV4::BuiltinFunction *, QV4::Scope
name = r->d()->d->name;
break;
}
- scope.result = Encode(scope.engine->newString(name));
+ return Encode(scope.engine->newString(name));
}
-void NodePrototype::method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_nodeValue(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
@@ -459,74 +461,81 @@ void NodePrototype::method_get_nodeValue(const QV4::BuiltinFunction *, QV4::Scop
r->d()->d->type == NodeImpl::Notation)
RETURN_RESULT(Encode::null());
- scope.result = Encode(scope.engine->newString(r->d()->d->data));
+ return Encode(scope.engine->newString(r->d()->d->data));
}
-void NodePrototype::method_get_nodeType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_nodeType(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
- scope.result = Encode(r->d()->d->type);
+ return Encode(r->d()->d->type);
}
-void NodePrototype::method_get_namespaceUri(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_namespaceUri(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
- scope.result = Encode(scope.engine->newString(r->d()->d->namespaceUri));
+ return Encode(scope.engine->newString(r->d()->d->namespaceUri));
}
-void NodePrototype::method_get_parentNode(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_parentNode(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
if (r->d()->d->parent)
- scope.result = Node::create(scope.engine, r->d()->d->parent);
+ return Node::create(scope.engine, r->d()->d->parent);
else
- scope.result = Encode::null();
+ return Encode::null();
}
-void NodePrototype::method_get_childNodes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_childNodes(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
- scope.result = NodeList::create(scope.engine, r->d()->d);
+ return NodeList::create(scope.engine, r->d()->d);
}
-void NodePrototype::method_get_firstChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_firstChild(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
if (r->d()->d->children.isEmpty())
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = Node::create(scope.engine, r->d()->d->children.constFirst());
+ return Node::create(scope.engine, r->d()->d->children.constFirst());
}
-void NodePrototype::method_get_lastChild(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_lastChild(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
if (r->d()->d->children.isEmpty())
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = Node::create(scope.engine, r->d()->d->children.constLast());
+ return Node::create(scope.engine, r->d()->d->children.constLast());
}
-void NodePrototype::method_get_previousSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_previousSibling(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
@@ -537,18 +546,18 @@ void NodePrototype::method_get_previousSibling(const QV4::BuiltinFunction *, QV4
for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) {
if (r->d()->d->parent->children.at(ii) == r->d()->d) {
if (ii == 0)
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1));
- return;
+ return Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1));
}
}
- scope.result = Encode::null();
+ return Encode::null();
}
-void NodePrototype::method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_nextSibling(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
@@ -559,26 +568,26 @@ void NodePrototype::method_get_nextSibling(const QV4::BuiltinFunction *, QV4::Sc
for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) {
if (r->d()->d->parent->children.at(ii) == r->d()->d) {
if ((ii + 1) == r->d()->d->parent->children.count())
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1));
- return;
+ return Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1));
}
}
- scope.result = Encode::null();
+ return Encode::null();
}
-void NodePrototype::method_get_attributes(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue NodePrototype::method_get_attributes(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
THROW_TYPE_ERROR();
if (r->d()->d->type != NodeImpl::Element)
- scope.result = Encode::null();
+ return Encode::null();
else
- scope.result = NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes);
+ return NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes);
}
ReturnedValue NodePrototype::getProto(ExecutionEngine *v4)
@@ -659,40 +668,44 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine)
return d->attrPrototype.value();
}
-void Attr::method_name(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Attr::method_name(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(r->d()->d->name);
+ return Encode(scope.engine->newString(r->d()->d->name));
}
-void Attr::method_value(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Attr::method_value(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(r->d()->d->data);
+ return Encode(scope.engine->newString(r->d()->d->data));
}
-void Attr::method_ownerElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Attr::method_ownerElement(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = Node::create(scope.engine, r->d()->d->parent);
+ return Node::create(scope.engine, r->d()->d->parent);
}
-void CharacterData::method_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue CharacterData::method_length(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = Encode(r->d()->d->data.length());
+ return Encode(r->d()->d->data.length());
}
ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
@@ -711,22 +724,24 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
return d->characterDataPrototype.value();
}
-void Text::method_isElementContentWhitespace(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Text::method_isElementContentWhitespace(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty());
+ return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty());
}
-void Text::method_wholeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Text::method_wholeText(const BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(r->d()->d->data);
+ return Encode(scope.engine->newString(r->d()->d->data));
}
ReturnedValue Text::prototype(ExecutionEngine *v4)
@@ -952,40 +967,44 @@ ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data)
return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue();
}
-void Document::method_documentElement(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Document::method_documentElement(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r || r->d()->d->type != NodeImpl::Document)
RETURN_UNDEFINED();
- scope.result = Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root);
+ return Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root);
}
-void Document::method_xmlStandalone(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Document::method_xmlStandalone(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r || r->d()->d->type != NodeImpl::Document)
RETURN_UNDEFINED();
- scope.result = Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone);
+ return Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone);
}
-void Document::method_xmlVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Document::method_xmlVersion(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r || r->d()->d->type != NodeImpl::Document)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version);
+ return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version));
}
-void Document::method_xmlEncoding(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue Document::method_xmlEncoding(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<Node> r(scope, callData->thisObject.as<Node>());
if (!r || r->d()->d->type != NodeImpl::Document)
RETURN_UNDEFINED();
- scope.result = scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding);
+ return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding));
}
class QQmlXMLHttpRequest : public QObject
@@ -1557,9 +1576,8 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont
return;
}
- QV4::ScopedCallData callData(scope);
- callData->thisObject = Encode::undefined();
- callback->call(scope, callData);
+ QV4::JSCallData jsCallData(scope);
+ callback->call(jsCallData);
if (scope.engine->hasException) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
@@ -1601,7 +1619,7 @@ struct QQmlXMLHttpRequestWrapper : Object {
Member(class, Pointer, Object *, proto)
DECLARE_HEAP_OBJECT(QQmlXMLHttpRequestCtor, FunctionObject) {
- DECLARE_MARK_TABLE(QQmlXMLHttpRequestCtor);
+ DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor);
void init(ExecutionEngine *engine);
};
@@ -1617,42 +1635,39 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
- static void construct(const Managed *that, Scope &scope, QV4::CallData *)
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *, int)
{
- Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>());
- if (!ctor) {
- scope.result = scope.engine->throwTypeError();
- return;
- }
+ Scope scope(f->engine());
+ const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager());
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
w->setPrototype(proto);
- scope.result = w.asReturnedValue();
+ return w.asReturnedValue();
}
- static void call(const Managed *, Scope &scope, QV4::CallData *) {
- scope.result = Primitive::undefinedValue();
+ static ReturnedValue call(const FunctionObject *, const Value *, const Value *, int) {
+ return Encode::undefined();
}
void setupProto();
- static void method_open(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_setRequestHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_send(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_abort(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_getResponseHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_getAllResponseHeaders(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_readyState(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_status(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_statusText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_responseText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_responseXML(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_response(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static ReturnedValue method_open(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_setRequestHeader(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_send(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_abort(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_getResponseHeader(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_getAllResponseHeaders(const BuiltinFunction *b, QV4::CallData *callData);
+
+ static ReturnedValue method_get_readyState(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_status(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_statusText(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_responseText(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_responseXML(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_response(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_get_responseType(const BuiltinFunction *b, QV4::CallData *callData);
+ static ReturnedValue method_set_responseType(const BuiltinFunction *b, QV4::CallData *callData);
};
}
@@ -1714,14 +1729,15 @@ void QQmlXMLHttpRequestCtor::setupProto()
// XMLHttpRequest methods
-void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_open(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- if (callData->argc < 2 || callData->argc > 5)
+ if (callData->argc() < 2 || callData->argc() > 5)
THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
// Argument 0 - Method
@@ -1744,15 +1760,15 @@ void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scop
bool async = true;
// Argument 2 - async (optional)
- if (callData->argc > 2) {
+ if (callData->argc() > 2) {
async = callData->args[2].booleanValue();
}
// Argument 3/4 - user/pass (optional)
QString username, password;
- if (callData->argc > 3)
+ if (callData->argc() > 3)
username = callData->args[3].toQStringNoThrow();
- if (callData->argc > 4)
+ if (callData->argc() > 4)
password = callData->args[4].toQStringNoThrow();
// Clear the fragment (if any)
@@ -1762,17 +1778,18 @@ void QQmlXMLHttpRequestCtor::method_open(const QV4::BuiltinFunction *, QV4::Scop
if (!username.isNull()) url.setUserName(username);
if (!password.isNull()) url.setPassword(password);
- scope.result = r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad);
+ return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad);
}
-void QQmlXMLHttpRequestCtor::method_setRequestHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- if (callData->argc != 2)
+ if (callData->argc() != 2)
THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag())
@@ -1811,8 +1828,9 @@ void QQmlXMLHttpRequestCtor::method_setRequestHeader(const QV4::BuiltinFunction
RETURN_UNDEFINED();
}
-void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_send(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1823,7 +1841,7 @@ void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scop
THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
QByteArray data;
- if (callData->argc > 0) {
+ if (callData->argc() > 0) {
if (const ArrayBuffer *buffer = callData->args[0].as<ArrayBuffer>()) {
data = buffer->asByteArray();
} else {
@@ -1831,27 +1849,29 @@ void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scop
}
}
- scope.result = r->send(w, scope.engine->callingQmlContext(), data);
+ return r->send(w, scope.engine->callingQmlContext(), data);
}
-void QQmlXMLHttpRequestCtor::method_abort(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- scope.result = r->abort(w, scope.engine->callingQmlContext());
+ return r->abort(w, scope.engine->callingQmlContext());
}
-void QQmlXMLHttpRequestCtor::method_getResponseHeader(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- if (callData->argc != 1)
+ if (callData->argc() != 1)
THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
if (r->readyState() != QQmlXMLHttpRequest::Loading &&
@@ -1859,17 +1879,18 @@ void QQmlXMLHttpRequestCtor::method_getResponseHeader(const QV4::BuiltinFunction
r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
- scope.result = scope.engine->newString(r->header(callData->args[0].toQStringNoThrow()));
+ return Encode(scope.engine->newString(r->header(callData->args[0].toQStringNoThrow())));
}
-void QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- if (callData->argc != 0)
+ if (callData->argc() != 0)
THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
if (r->readyState() != QQmlXMLHttpRequest::Loading &&
@@ -1877,22 +1898,24 @@ void QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const QV4::BuiltinFunc
r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
- scope.result = scope.engine->newString(r->headers());
+ return Encode(scope.engine->newString(r->headers()));
}
// XMLHttpRequest properties
-void QQmlXMLHttpRequestCtor::method_get_readyState(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_readyState(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- scope.result = Encode(r->readyState());
+ return Encode(r->readyState());
}
-void QQmlXMLHttpRequestCtor::method_get_status(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_status(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1903,13 +1926,14 @@ void QQmlXMLHttpRequestCtor::method_get_status(const QV4::BuiltinFunction *, QV4
THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
if (r->errorFlag())
- scope.result = Encode(0);
+ return Encode(0);
else
- scope.result = Encode(r->replyStatus());
+ return Encode(r->replyStatus());
}
-void QQmlXMLHttpRequestCtor::method_get_statusText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_statusText(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1920,13 +1944,14 @@ void QQmlXMLHttpRequestCtor::method_get_statusText(const QV4::BuiltinFunction *,
THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
if (r->errorFlag())
- scope.result = scope.engine->newString(QString());
+ return Encode(scope.engine->newString(QString()));
else
- scope.result = scope.engine->newString(r->replyStatusText());
+ return Encode(scope.engine->newString(r->replyStatusText()));
}
-void QQmlXMLHttpRequestCtor::method_get_responseText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseText(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1934,13 +1959,14 @@ void QQmlXMLHttpRequestCtor::method_get_responseText(const QV4::BuiltinFunction
if (r->readyState() != QQmlXMLHttpRequest::Loading &&
r->readyState() != QQmlXMLHttpRequest::Done)
- scope.result = scope.engine->newString(QString());
+ return Encode(scope.engine->newString(QString()));
else
- scope.result = scope.engine->newString(r->responseBody());
+ return Encode(scope.engine->newString(r->responseBody()));
}
-void QQmlXMLHttpRequestCtor::method_get_responseXML(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1949,16 +1975,17 @@ void QQmlXMLHttpRequestCtor::method_get_responseXML(const QV4::BuiltinFunction *
if (!r->receivedXml() ||
(r->readyState() != QQmlXMLHttpRequest::Loading &&
r->readyState() != QQmlXMLHttpRequest::Done)) {
- scope.result = Encode::null();
+ return Encode::null();
} else {
if (r->responseType().isEmpty())
r->setResponseType(QLatin1String("document"));
- scope.result = r->xmlResponseBody(scope.engine);
+ return r->xmlResponseBody(scope.engine);
}
}
-void QQmlXMLHttpRequestCtor::method_get_response(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
@@ -1983,29 +2010,31 @@ void QQmlXMLHttpRequestCtor::method_get_response(const QV4::BuiltinFunction *, Q
}
-void QQmlXMLHttpRequestCtor::method_get_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- scope.result = scope.engine->newString(r->responseType());
+ return Encode(scope.engine->newString(r->responseType()));
}
-void QQmlXMLHttpRequestCtor::method_set_responseType(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const BuiltinFunction *b, QV4::CallData *callData)
{
+ Scope scope(b);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, callData->thisObject.as<QQmlXMLHttpRequestWrapper>());
if (!w)
V4THROW_REFERENCE("Not an XMLHttpRequest object");
QQmlXMLHttpRequest *r = w->d()->request;
- if (callData->argc < 1)
+ if (callData->argc() < 1)
THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
// Argument 0 - response type
r->setResponseType(callData->args[0].toQStringNoThrow());
- scope.result = Encode::undefined();
+ return Encode::undefined();
}
void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 25d99e0c87..3627f29cb2 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -87,8 +87,7 @@ DEFINE_OBJECT_VTABLE(QtObject);
#define THROW_TYPE_ERROR_WITH_MESSAGE(msg) \
do { \
- scope.result = scope.engine->throwTypeError(QString::fromUtf8(msg)); \
- return; \
+ return scope.engine->throwTypeError(QString::fromUtf8(msg)); \
} while (false)
struct StaticQtMetaObject : public QObject
@@ -229,12 +228,12 @@ void QtObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint
\qmlmethod bool Qt::isQtObject(object)
Returns true if \c object is a valid reference to a Qt or QML object, otherwise false.
*/
-void QtObject::method_isQtObject(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_isQtObject(const BuiltinFunction *, CallData *callData)
{
- if (callData->argc == 0)
+ if (callData->argc() == 0)
RETURN_RESULT(QV4::Encode(false));
- scope.result = QV4::Encode(callData->args[0].as<QV4::QObjectWrapper>() != 0);
+ return QV4::Encode(callData->args[0].as<QV4::QObjectWrapper>() != 0);
}
/*!
@@ -243,9 +242,10 @@ void QtObject::method_isQtObject(const BuiltinFunction *, Scope &scope, CallData
Returns a color with the specified \c red, \c green, \c blue and \c alpha components.
All components should be in the range 0-1 inclusive.
*/
-void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_rgba(const BuiltinFunction *builtin, CallData *callData)
{
- int argCount = callData->argc;
+ QV4::Scope scope(builtin);
+ int argCount = callData->argc();
if (argCount < 3 || argCount > 4)
THROW_GENERIC_ERROR("Qt.rgba(): Invalid arguments");
@@ -263,7 +263,7 @@ void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *call
if (a < 0.0) a=0.0;
if (a > 1.0) a=1.0;
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a));
+ return scope.engine->fromVariant(QQml_colorProvider()->fromRgbF(r, g, b, a));
}
/*!
@@ -272,9 +272,10 @@ void QtObject::method_rgba(const BuiltinFunction *, Scope &scope, CallData *call
Returns a color with the specified \c hue, \c saturation, \c lightness and \c alpha components.
All components should be in the range 0-1 inclusive.
*/
-void QtObject::method_hsla(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_hsla(const BuiltinFunction *b, CallData *callData)
{
- int argCount = callData->argc;
+ QV4::Scope scope(b);
+ int argCount = callData->argc();
if (argCount < 3 || argCount > 4)
THROW_GENERIC_ERROR("Qt.hsla(): Invalid arguments");
@@ -292,7 +293,7 @@ void QtObject::method_hsla(const BuiltinFunction *, Scope &scope, CallData *call
if (a < 0.0) a=0.0;
if (a > 1.0) a=1.0;
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a));
+ return scope.engine->fromVariant(QQml_colorProvider()->fromHslF(h, s, l, a));
}
/*!
@@ -303,9 +304,10 @@ All components should be in the range 0-1 inclusive.
\since 5.5
*/
-void QtObject::method_hsva(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_hsva(const BuiltinFunction *b, CallData *callData)
{
- int argCount = callData->argc;
+ QV4::Scope scope(b);
+ int argCount = callData->argc();
if (argCount < 3 || argCount > 4)
THROW_GENERIC_ERROR("Qt.hsva(): Invalid arguments");
@@ -319,7 +321,7 @@ void QtObject::method_hsva(const BuiltinFunction *, Scope &scope, CallData *call
v = qBound(0.0, v, 1.0);
a = qBound(0.0, a, 1.0);
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a));
+ return scope.engine->fromVariant(QQml_colorProvider()->fromHsvF(h, s, v, a));
}
/*!
@@ -330,9 +332,10 @@ may be either color values or string values. If a string value is supplied it
must be convertible to a color, as described for the \l{colorbasictypedocs}{color}
basic type.
*/
-void QtObject::method_colorEqual(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_colorEqual(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.colorEqual(): Invalid arguments");
bool ok = false;
@@ -358,7 +361,7 @@ void QtObject::method_colorEqual(const BuiltinFunction *, Scope &scope, CallData
}
bool equal = (lhs == rhs);
- scope.result = QV4::Encode(equal);
+ return QV4::Encode(equal);
}
/*!
@@ -368,9 +371,10 @@ Returns a \c rect with the top-left corner at \c x, \c y and the specified \c wi
The returned object has \c x, \c y, \c width and \c height attributes with the given values.
*/
-void QtObject::method_rect(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_rect(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 4)
+ QV4::Scope scope(b);
+ if (callData->argc() != 4)
THROW_GENERIC_ERROR("Qt.rect(): Invalid arguments");
double x = callData->args[0].toNumber();
@@ -378,37 +382,39 @@ void QtObject::method_rect(const BuiltinFunction *, Scope &scope, CallData *call
double w = callData->args[2].toNumber();
double h = callData->args[3].toNumber();
- scope.result = scope.engine->fromVariant(QVariant::fromValue(QRectF(x, y, w, h)));
+ return scope.engine->fromVariant(QVariant::fromValue(QRectF(x, y, w, h)));
}
/*!
\qmlmethod point Qt::point(int x, int y)
Returns a Point with the specified \c x and \c y coordinates.
*/
-void QtObject::method_point(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_point(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.point(): Invalid arguments");
double x = callData->args[0].toNumber();
double y = callData->args[1].toNumber();
- scope.result = scope.engine->fromVariant(QVariant::fromValue(QPointF(x, y)));
+ return scope.engine->fromVariant(QVariant::fromValue(QPointF(x, y)));
}
/*!
\qmlmethod Qt::size(int width, int height)
Returns a Size with the specified \c width and \c height.
*/
-void QtObject::method_size(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_size(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.size(): Invalid arguments");
double w = callData->args[0].toNumber();
double h = callData->args[1].toNumber();
- scope.result = scope.engine->fromVariant(QVariant::fromValue(QSizeF(w, h)));
+ return scope.engine->fromVariant(QVariant::fromValue(QSizeF(w, h)));
}
/*!
@@ -419,9 +425,10 @@ key-value pairs where valid keys are the \l{fontbasictypedocs}{font} type's
subproperty names, and the values are valid values for each subproperty.
Invalid keys will be ignored.
*/
-void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_font(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1 || !callData->args[0].isObject())
+ QV4::Scope scope(b);
+ if (callData->argc() != 1 || !callData->args[0].isObject())
THROW_GENERIC_ERROR("Qt.font(): Invalid arguments");
QV4::ExecutionEngine *v4 = scope.engine;
@@ -429,7 +436,7 @@ void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *call
QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(callData->args[0]), v4, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified");
- scope.result = scope.engine->fromVariant(v);
+ return scope.engine->fromVariant(v);
}
@@ -438,9 +445,10 @@ void QtObject::method_font(const BuiltinFunction *, Scope &scope, CallData *call
\qmlmethod Qt::vector2d(real x, real y)
Returns a Vector2D with the specified \c x and \c y.
*/
-void QtObject::method_vector2d(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_vector2d(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.vector2d(): Invalid arguments");
float xy[3]; // qvector2d uses float internally
@@ -448,16 +456,17 @@ void QtObject::method_vector2d(const BuiltinFunction *, Scope &scope, CallData *
xy[1] = callData->args[1].toNumber();
const void *params[] = { xy };
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector2D, 1, params));
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector2D, 1, params));
}
/*!
\qmlmethod Qt::vector3d(real x, real y, real z)
Returns a Vector3D with the specified \c x, \c y and \c z.
*/
-void QtObject::method_vector3d(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_vector3d(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 3)
+ QV4::Scope scope(b);
+ if (callData->argc() != 3)
THROW_GENERIC_ERROR("Qt.vector3d(): Invalid arguments");
float xyz[3]; // qvector3d uses float internally
@@ -466,16 +475,17 @@ void QtObject::method_vector3d(const BuiltinFunction *, Scope &scope, CallData *
xyz[2] = callData->args[2].toNumber();
const void *params[] = { xyz };
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params));
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector3D, 1, params));
}
/*!
\qmlmethod Qt::vector4d(real x, real y, real z, real w)
Returns a Vector4D with the specified \c x, \c y, \c z and \c w.
*/
-void QtObject::method_vector4d(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_vector4d(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 4)
+ QV4::Scope scope(b);
+ if (callData->argc() != 4)
THROW_GENERIC_ERROR("Qt.vector4d(): Invalid arguments");
float xyzw[4]; // qvector4d uses float internally
@@ -485,16 +495,17 @@ void QtObject::method_vector4d(const BuiltinFunction *, Scope &scope, CallData *
xyzw[3] = callData->args[3].toNumber();
const void *params[] = { xyzw };
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params));
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QVector4D, 1, params));
}
/*!
\qmlmethod Qt::quaternion(real scalar, real x, real y, real z)
Returns a Quaternion with the specified \c scalar, \c x, \c y, and \c z.
*/
-void QtObject::method_quaternion(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_quaternion(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 4)
+ QV4::Scope scope(b);
+ if (callData->argc() != 4)
THROW_GENERIC_ERROR("Qt.quaternion(): Invalid arguments");
qreal sxyz[4]; // qquaternion uses qreal internally
@@ -504,7 +515,7 @@ void QtObject::method_quaternion(const BuiltinFunction *, Scope &scope, CallData
sxyz[3] = callData->args[3].toNumber();
const void *params[] = { sxyz };
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params));
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QQuaternion, 1, params));
}
/*!
@@ -516,25 +527,23 @@ matrix values.
Finally, the function may be called with no arguments and the resulting
matrix will be the identity matrix.
*/
-void QtObject::method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_matrix4x4(const BuiltinFunction *b, CallData *callData)
{
- QV4::ExecutionEngine *v4 = scope.engine;
+ QV4::Scope scope(b);
- if (callData->argc == 0) {
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, nullptr));
- return;
+ if (callData->argc() == 0) {
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 0, nullptr));
}
- if (callData->argc == 1 && callData->args[0].isObject()) {
+ if (callData->argc() == 1 && callData->args[0].isObject()) {
bool ok = false;
- QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(callData->args[0]), v4, &ok);
+ QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(callData->args[0]), scope.engine, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array");
- scope.result = scope.engine->fromVariant(v);
- return;
+ return scope.engine->fromVariant(v);
}
- if (callData->argc != 16)
+ if (callData->argc() != 16)
THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid arguments");
qreal vals[16]; // qmatrix4x4 uses qreal internally
@@ -556,7 +565,7 @@ void QtObject::method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData
vals[15] = callData->args[15].toNumber();
const void *params[] = { vals };
- scope.result = scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params));
+ return scope.engine->fromVariant(QQml_valueTypeProvider()->createValueType(QMetaType::QMatrix4x4, 1, params));
}
/*!
@@ -573,9 +582,10 @@ by factor and converts the color back to RGB.
If \c factor is not supplied, returns a color 50% lighter than \c baseColor (factor 1.5).
*/
-void QtObject::method_lighter(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_lighter(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1 && callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1 && callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.lighter(): Invalid arguments");
QVariant v = scope.engine->toVariant(callData->args[0], -1);
@@ -583,19 +593,17 @@ void QtObject::method_lighter(const BuiltinFunction *, Scope &scope, CallData *c
bool ok = false;
v = QQmlStringConverters::colorFromString(v.toString(), &ok);
if (!ok) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
} else if (v.userType() != QVariant::Color) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
qreal factor = 1.5;
- if (callData->argc == 2)
+ if (callData->argc() == 2)
factor = callData->args[1].toNumber();
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->lighter(v, factor));
+ return scope.engine->fromVariant(QQml_colorProvider()->lighter(v, factor));
}
/*!
@@ -613,9 +621,10 @@ by factor and converts the color back to RGB.
If \c factor is not supplied, returns a color 50% darker than \c baseColor (factor 2.0).
*/
-void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_darker(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1 && callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1 && callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.darker(): Invalid arguments");
QVariant v = scope.engine->toVariant(callData->args[0], -1);
@@ -623,19 +632,17 @@ void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *ca
bool ok = false;
v = QQmlStringConverters::colorFromString(v.toString(), &ok);
if (!ok) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
} else if (v.userType() != QVariant::Color) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
qreal factor = 2.0;
- if (callData->argc == 2)
+ if (callData->argc() == 2)
factor = callData->args[1].toNumber();
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->darker(v, factor));
+ return scope.engine->fromVariant(QQml_colorProvider()->darker(v, factor));
}
/*!
@@ -662,9 +669,10 @@ void QtObject::method_darker(const BuiltinFunction *, Scope &scope, CallData *ca
Tint is most useful when a subtle change is intended to be conveyed due to some event; you can then use tinting to more effectively tune the visible color.
*/
-void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_tint(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 2)
+ QV4::Scope scope(b);
+ if (callData->argc() != 2)
THROW_GENERIC_ERROR("Qt.tint(): Invalid arguments");
// base color
@@ -673,12 +681,10 @@ void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *call
bool ok = false;
v1 = QQmlStringConverters::colorFromString(v1.toString(), &ok);
if (!ok) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
} else if (v1.userType() != QVariant::Color) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
// tint color
@@ -687,15 +693,13 @@ void QtObject::method_tint(const BuiltinFunction *, Scope &scope, CallData *call
bool ok = false;
v2 = QQmlStringConverters::colorFromString(v2.toString(), &ok);
if (!ok) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
} else if (v2.userType() != QVariant::Color) {
- scope.result = QV4::Encode::null();
- return;
+ return QV4::Encode::null();
}
- scope.result = scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2));
+ return scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2));
}
/*!
@@ -714,15 +718,16 @@ If \a format is not specified, \a date is formatted using
\sa Locale
*/
-void QtObject::method_formatDate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_formatDate(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1 || callData->argc > 2)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1 || callData->argc() > 2)
THROW_GENERIC_ERROR("Qt.formatDate(): Invalid arguments");
Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
QDate date = scope.engine->toVariant(callData->args[0], -1).toDateTime().date();
QString formattedDate;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
QV4::ScopedString s(scope, callData->args[1]);
if (s) {
QString format = s->toQString();
@@ -738,7 +743,7 @@ void QtObject::method_formatDate(const BuiltinFunction *, Scope &scope, CallData
formattedDate = date.toString(enumFormat);
}
- scope.result = scope.engine->newString(formattedDate);
+ return Encode(scope.engine->newString(formattedDate));
}
/*!
@@ -756,9 +761,10 @@ If \a format is not specified, \a time is formatted using
\sa Locale
*/
-void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_formatTime(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1 || callData->argc > 2)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1 || callData->argc() > 2)
THROW_GENERIC_ERROR("Qt.formatTime(): Invalid arguments");
QVariant argVariant = scope.engine->toVariant(callData->args[0], -1);
@@ -770,7 +776,7 @@ void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData
Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
QString formattedTime;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
QV4::ScopedString s(scope, callData->args[1]);
if (s) {
QString format = s->toQString();
@@ -786,7 +792,7 @@ void QtObject::method_formatTime(const BuiltinFunction *, Scope &scope, CallData
formattedTime = time.toString(enumFormat);
}
- scope.result = scope.engine->newString(formattedTime);
+ return Encode(scope.engine->newString(formattedTime));
}
/*!
@@ -881,15 +887,16 @@ with the \a format values below to produce the following results:
\sa Locale
*/
-void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_formatDateTime(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1 || callData->argc > 2)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1 || callData->argc() > 2)
THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid arguments");
Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate;
QDateTime dt = scope.engine->toVariant(callData->args[0], -1).toDateTime();
QString formattedDt;
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
QV4::ScopedString s(scope, callData->args[1]);
if (s) {
QString format = s->toQString();
@@ -905,7 +912,7 @@ void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, Call
formattedDt = dt.toString(enumFormat);
}
- scope.result = scope.engine->newString(formattedDt);
+ return Encode(scope.engine->newString(formattedDt));
}
/*!
@@ -919,94 +926,97 @@ void QtObject::method_formatDateTime(const BuiltinFunction *, Scope &scope, Call
still fail to launch or fail to open the requested URL. This result will not be reported back
to the application.
*/
-void QtObject::method_openUrlExternally(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_openUrlExternally(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1) {
- scope.result = QV4::Encode(false);
- return;
+ QV4::Scope scope(b);
+ if (callData->argc() != 1) {
+ return QV4::Encode(false);
}
- method_resolvedUrl(b, scope, callData);
- QUrl url(scope.result.toQStringNoThrow());
- scope.result = scope.engine->fromVariant(QQml_guiProvider()->openUrlExternally(url));
+ ScopedValue result(scope, method_resolvedUrl(b, callData));
+ QUrl url(result->toQStringNoThrow());
+ return scope.engine->fromVariant(QQml_guiProvider()->openUrlExternally(url));
}
/*!
\qmlmethod url Qt::resolvedUrl(url url)
Returns \a url resolved relative to the URL of the caller.
*/
-void QtObject::method_resolvedUrl(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_resolvedUrl(const BuiltinFunction *b, CallData *callData)
{
- ExecutionEngine *v4 = scope.engine;
+ QV4::Scope scope(b);
- QUrl url = v4->toVariant(callData->args[0], -1).toUrl();
- QQmlEngine *e = v4->qmlEngine();
+ QUrl url = scope.engine->toVariant(callData->args[0], -1).toUrl();
+ QQmlEngine *e = scope.engine->qmlEngine();
QQmlEnginePrivate *p = 0;
if (e) p = QQmlEnginePrivate::get(e);
if (p) {
- QQmlContextData *ctxt = v4->callingQmlContext();
+ QQmlContextData *ctxt = scope.engine->callingQmlContext();
if (ctxt)
- scope.result = v4->newString(ctxt->resolvedUrl(url).toString());
+ return Encode(scope.engine->newString(ctxt->resolvedUrl(url).toString()));
else
- scope.result = v4->newString(url.toString());
- return;
+ return Encode(scope.engine->newString(url.toString()));
}
- scope.result = v4->newString(e->baseUrl().resolved(url).toString());
+ return Encode(scope.engine->newString(e->baseUrl().resolved(url).toString()));
}
/*!
\qmlmethod list<string> Qt::fontFamilies()
Returns a list of the font families available to the application.
*/
-void QtObject::method_fontFamilies(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_fontFamilies(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 0)
+ QV4::Scope scope(b);
+ if (callData->argc() != 0)
THROW_GENERIC_ERROR("Qt.fontFamilies(): Invalid arguments");
- scope.result = scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies()));
+ return scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies()));
}
/*!
\qmlmethod string Qt::md5(data)
Returns a hex string of the md5 hash of \c data.
*/
-void QtObject::method_md5(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_md5(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("Qt.md5(): Invalid arguments");
QByteArray data = callData->args[0].toQStringNoThrow().toUtf8();
QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5);
- scope.result = scope.engine->newString(QLatin1String(result.toHex()));
+ return Encode(scope.engine->newString(QLatin1String(result.toHex())));
}
/*!
\qmlmethod string Qt::btoa(data)
Binary to ASCII - this function returns a base64 encoding of \c data.
*/
-void QtObject::method_btoa(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_btoa(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("Qt.btoa(): Invalid arguments");
QByteArray data = callData->args[0].toQStringNoThrow().toUtf8();
- scope.result = scope.engine->newString(QLatin1String(data.toBase64()));
+ return Encode(scope.engine->newString(QLatin1String(data.toBase64())));
}
/*!
\qmlmethod string Qt::atob(data)
ASCII to binary - this function decodes the base64 encoded \a data string and returns it.
*/
-void QtObject::method_atob(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_atob(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("Qt.atob(): Invalid arguments");
QByteArray data = callData->args[0].toQStringNoThrow().toLatin1();
- scope.result = scope.engine->newString(QString::fromUtf8(QByteArray::fromBase64(data)));
+ return Encode(scope.engine->newString(QString::fromUtf8(QByteArray::fromBase64(data))));
}
/*!
@@ -1018,10 +1028,10 @@ QQmlEngine::quit() signal to the QCoreApplication::quit() slot.
\sa exit()
*/
-void QtObject::method_quit(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue QtObject::method_quit(const BuiltinFunction *b, CallData *)
{
- QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendQuit();
- scope.result = Encode::undefined();
+ QQmlEnginePrivate::get(b->engine()->qmlEngine())->sendQuit();
+ return Encode::undefined();
}
/*!
@@ -1035,15 +1045,16 @@ void QtObject::method_quit(const BuiltinFunction *, Scope &scope, CallData *)
\sa quit()
*/
-void QtObject::method_exit(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_exit(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("Qt.exit(): Invalid arguments");
int retCode = callData->args[0].toNumber();
QQmlEnginePrivate::get(scope.engine->qmlEngine())->sendExit(retCode);
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
/*!
@@ -1070,9 +1081,10 @@ If this is the case, consider using \l{QtQml::Qt::createComponent()}{Qt.createCo
See \l {Dynamic QML Object Creation from JavaScript} for more information on using this function.
*/
-void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_createQmlObject(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 2 || callData->argc > 3)
+ QV4::Scope scope(b);
+ if (callData->argc() < 2 || callData->argc() > 3)
THROW_GENERIC_ERROR("Qt.createQmlObject(): Invalid arguments");
struct Error {
@@ -1121,7 +1133,7 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal
RETURN_RESULT(Encode::null());
QUrl url;
- if (callData->argc > 2)
+ if (callData->argc() > 2)
url = QUrl(callData->args[2].toQStringNoThrow());
else
url = QUrl(QLatin1String("inline"));
@@ -1174,13 +1186,12 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal
if (component.isError()) {
ScopedValue v(scope, Error::create(scope.engine, component.errors()));
- scope.result = scope.engine->throwError(v);
- return;
+ return scope.engine->throwError(v);
}
Q_ASSERT(obj);
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, obj);
+ return QV4::QObjectWrapper::wrap(scope.engine, obj);
}
/*!
@@ -1227,9 +1238,10 @@ See \l {Dynamic QML Object Creation from JavaScript} for more information on usi
To create a QML object from an arbitrary string of QML (instead of a file),
use \l{QtQml::Qt::createQmlObject()}{Qt.createQmlObject()}.
*/
-void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_createComponent(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1 || callData->argc > 3)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1 || callData->argc() > 3)
THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments");
QV8Engine *v8engine = scope.engine->v8Engine;
@@ -1249,8 +1261,8 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal
QObject *parentArg = 0;
int consumedCount = 1;
- if (callData->argc > 1) {
- ScopedValue lastArg(scope, callData->args[callData->argc-1]);
+ if (callData->argc() > 1) {
+ ScopedValue lastArg(scope, callData->args[callData->argc()-1]);
// The second argument could be the mode enum
if (callData->args[1].isInteger()) {
@@ -1261,11 +1273,11 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal
consumedCount += 1;
} else {
// The second argument could be the parent only if there are exactly two args
- if ((callData->argc != 2) || !(lastArg->isObject() || lastArg->isNull()))
+ if ((callData->argc() != 2) || !(lastArg->isObject() || lastArg->isNull()))
THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments");
}
- if (consumedCount < callData->argc) {
+ if (consumedCount < callData->argc()) {
if (lastArg->isObject()) {
Scoped<QObjectWrapper> qobjectWrapper(scope, lastArg);
if (qobjectWrapper)
@@ -1286,7 +1298,7 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal
QQmlData::get(c, true)->explicitIndestructibleSet = false;
QQmlData::get(c)->indestructible = false;
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, c);
+ return QV4::QObjectWrapper::wrap(scope.engine, c);
}
/*!
@@ -1309,18 +1321,19 @@ void QtObject::method_createComponent(const BuiltinFunction *, Scope &scope, Cal
\sa Locale
*/
-void QtObject::method_locale(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_locale(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
QString code;
- if (callData->argc > 1)
+ if (callData->argc() > 1)
THROW_GENERIC_ERROR("locale() requires 0 or 1 argument");
- if (callData->argc == 1 && !callData->args[0].isString())
+ if (callData->argc() == 1 && !callData->args[0].isString())
THROW_TYPE_ERROR_WITH_MESSAGE("locale(): argument (locale code) must be a string");
- if (callData->argc == 1)
+ if (callData->argc() == 1)
code = callData->args[0].toQStringNoThrow();
- scope.result = QQmlLocale::locale(scope.engine, code);
+ return QQmlLocale::locale(scope.engine, code);
}
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction)
@@ -1332,8 +1345,8 @@ void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction
QQmlSourceLocation QQmlBindingFunction::currentLocation() const
{
- QV4::StackFrame frame = engine()->currentStackFrame();
- return QQmlSourceLocation(frame.source, frame.line, 0);
+ QV4::CppStackFrame *frame = engine()->currentStackFrame;
+ return QQmlSourceLocation(frame->source(), frame->lineNumber(), 0);
}
DEFINE_OBJECT_VTABLE(QQmlBindingFunction);
@@ -1382,20 +1395,22 @@ DEFINE_OBJECT_VTABLE(QQmlBindingFunction);
\since 5.0
*/
-void QtObject::method_binding(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_binding(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("binding() requires 1 argument");
const QV4::FunctionObject *f = callData->args[0].as<FunctionObject>();
if (!f)
THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function");
- scope.result = scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f);
+ return Encode(scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f));
}
-void QtObject::method_get_platform(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_get_platform(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
// ### inefficient. Should be just a value based getter
Object *o = callData->thisObject.as<Object>();
if (!o)
@@ -1408,11 +1423,12 @@ void QtObject::method_get_platform(const BuiltinFunction *, Scope &scope, CallDa
// Only allocate a platform object once
qt->d()->platform = new QQmlPlatform(scope.engine->jsEngine());
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->platform);
+ return QV4::QObjectWrapper::wrap(scope.engine, qt->d()->platform);
}
-void QtObject::method_get_application(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_get_application(const BuiltinFunction *b, CallData *callData)
{
+ QV4::Scope scope(b);
// ### inefficient. Should be just a value based getter
Object *o = callData->thisObject.as<Object>();
if (!o)
@@ -1425,19 +1441,19 @@ void QtObject::method_get_application(const BuiltinFunction *, Scope &scope, Cal
// Only allocate an application object once
qt->d()->application = QQml_guiProvider()->application(scope.engine->jsEngine());
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, qt->d()->application);
+ return QV4::QObjectWrapper::wrap(scope.engine, qt->d()->application);
}
-void QtObject::method_get_inputMethod(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue QtObject::method_get_inputMethod(const BuiltinFunction *b, CallData *)
{
QObject *o = QQml_guiProvider()->inputMethod();
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, o);
+ return QV4::QObjectWrapper::wrap(b->engine(), o);
}
-void QtObject::method_get_styleHints(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue QtObject::method_get_styleHints(const BuiltinFunction *b, CallData *)
{
QObject *o = QQml_guiProvider()->styleHints();
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, o);
+ return QV4::QObjectWrapper::wrap(b->engine(), o);
}
@@ -1497,15 +1513,16 @@ static QString jsStack(QV4::ExecutionEngine *engine) {
return stack;
}
-static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *callData,
- ConsoleLogTypes logType, bool printStack = false)
+static ReturnedValue writeToConsole(const BuiltinFunction *b, CallData *callData,
+ ConsoleLogTypes logType, bool printStack = false)
{
QLoggingCategory *loggingCategory = 0;
QString result;
+ QV4::Scope scope(b);
QV4::ExecutionEngine *v4 = scope.engine;
int start = 0;
- if (callData->argc > 0) {
+ if (callData->argc() > 0) {
if (const QObjectWrapper* wrapper = callData->args[0].as<QObjectWrapper>()) {
if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) {
if (category->category())
@@ -1518,7 +1535,7 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call
}
- for (int i = start; i < callData->argc; ++i) {
+ for (int i = start, ei = callData->argc(); i < ei; ++i) {
if (i != start)
result.append(QLatin1Char(' '));
@@ -1536,10 +1553,10 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call
if (!loggingCategory)
loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory;
- QV4::StackFrame frame = v4->currentStackFrame();
- const QByteArray baSource = frame.source.toUtf8();
- const QByteArray baFunction = frame.function.toUtf8();
- QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData(), loggingCategory->categoryName());
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
+ const QByteArray baSource = frame->source().toUtf8();
+ const QByteArray baFunction = frame->function().toUtf8();
+ QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData(), loggingCategory->categoryName());
switch (logType) {
case Log:
@@ -1562,37 +1579,38 @@ static void writeToConsole(const BuiltinFunction *, Scope &scope, CallData *call
break;
}
- scope.result = QV4::Encode::undefined();
+ return Encode::undefined();
}
DEFINE_OBJECT_VTABLE(ConsoleObject);
-void ConsoleObject::method_error(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_error(const BuiltinFunction *b, CallData *callData)
{
- writeToConsole(b, scope, callData, Error);
+ return writeToConsole(b, callData, Error);
}
-void ConsoleObject::method_log(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_log(const BuiltinFunction *b, CallData *callData)
{
//console.log
//console.debug
//print
- writeToConsole(b, scope, callData, Log);
+ return writeToConsole(b, callData, Log);
}
-void ConsoleObject::method_info(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_info(const BuiltinFunction *b, CallData *callData)
{
- writeToConsole(b, scope, callData, Info);
+ return writeToConsole(b, callData, Info);
}
-void ConsoleObject::method_profile(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue ConsoleObject::method_profile(const BuiltinFunction *b, CallData *)
{
+ QV4::Scope scope(b);
QV4::ExecutionEngine *v4 = scope.engine;
- QV4::StackFrame frame = v4->currentStackFrame();
- const QByteArray baSource = frame.source.toUtf8();
- const QByteArray baFunction = frame.function.toUtf8();
- QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData());
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
+ const QByteArray baSource = frame->source().toUtf8();
+ const QByteArray baFunction = frame->function().toUtf8();
+ QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData());
QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>();
if (!service) {
logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX.");
@@ -1601,17 +1619,18 @@ void ConsoleObject::method_profile(const BuiltinFunction *, Scope &scope, CallDa
logger.debug("Profiling started.");
}
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_profileEnd(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue ConsoleObject::method_profileEnd(const BuiltinFunction *b, CallData *)
{
+ QV4::Scope scope(b);
QV4::ExecutionEngine *v4 = scope.engine;
- QV4::StackFrame frame = v4->currentStackFrame();
- const QByteArray baSource = frame.source.toUtf8();
- const QByteArray baFunction = frame.function.toUtf8();
- QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData());
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
+ const QByteArray baSource = frame->source().toUtf8();
+ const QByteArray baFunction = frame->function().toUtf8();
+ QMessageLogger logger(baSource.constData(), frame->lineNumber(), baFunction.constData());
QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>();
if (!service) {
@@ -1621,24 +1640,26 @@ void ConsoleObject::method_profileEnd(const BuiltinFunction *, Scope &scope, Cal
logger.debug("Profiling ended.");
}
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_time(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_time(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("console.time(): Invalid arguments");
QV8Engine *v8engine = scope.engine->v8Engine;
QString name = callData->args[0].toQStringNoThrow();
v8engine->startTimer(name);
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_timeEnd(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_timeEnd(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("console.timeEnd(): Invalid arguments");
QV8Engine *v8engine = scope.engine->v8Engine;
@@ -1649,65 +1670,68 @@ void ConsoleObject::method_timeEnd(const BuiltinFunction *, Scope &scope, CallDa
if (wasRunning) {
qDebug("%s: %llims", qPrintable(name), elapsed);
}
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_count(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_count(const BuiltinFunction *b, CallData *callData)
{
// first argument: name to print. Ignore any additional arguments
QString name;
- if (callData->argc > 0)
+ if (callData->argc() > 0)
name = callData->args[0].toQStringNoThrow();
+ Scope scope(b);
QV4::ExecutionEngine *v4 = scope.engine;
QV8Engine *v8engine = scope.engine->v8Engine;
- QV4::StackFrame frame = v4->currentStackFrame();
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
- QString scriptName = frame.source;
+ QString scriptName = frame->source();
- int value = v8engine->consoleCountHelper(scriptName, frame.line, frame.column);
+ int value = v8engine->consoleCountHelper(scriptName, frame->lineNumber(), -1);
QString message = name + QLatin1String(": ") + QString::number(value);
- QMessageLogger(qPrintable(scriptName), frame.line,
- qPrintable(frame.function))
+ QMessageLogger(qPrintable(scriptName), frame->lineNumber(),
+ qPrintable(frame->function()))
.debug("%s", qPrintable(message));
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_trace(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_trace(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 0)
+ QV4::Scope scope(b);
+ if (callData->argc() != 0)
THROW_GENERIC_ERROR("console.trace(): Invalid arguments");
QV4::ExecutionEngine *v4 = scope.engine;
QString stack = jsStack(v4);
- QV4::StackFrame frame = v4->currentStackFrame();
- QMessageLogger(frame.source.toUtf8().constData(), frame.line,
- frame.function.toUtf8().constData())
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
+ QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(),
+ frame->function().toUtf8().constData())
.debug("%s", qPrintable(stack));
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_warn(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_warn(const BuiltinFunction *b, CallData *callData)
{
- return writeToConsole(b, scope, callData, Warn);
+ return writeToConsole(b, callData, Warn);
}
-void ConsoleObject::method_assert(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_assert(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc == 0)
+ QV4::Scope scope(b);
+ if (callData->argc() == 0)
THROW_GENERIC_ERROR("console.assert(): Missing argument");
QV4::ExecutionEngine *v4 = scope.engine;
if (!callData->args[0].toBoolean()) {
QString message;
- for (int i = 1; i < callData->argc; ++i) {
+ for (int i = 1, ei = callData->argc(); i < ei; ++i) {
if (i != 1)
message.append(QLatin1Char(' '));
@@ -1716,23 +1740,22 @@ void ConsoleObject::method_assert(const BuiltinFunction *, Scope &scope, CallDat
QString stack = jsStack(v4);
- QV4::StackFrame frame = v4->currentStackFrame();
- QMessageLogger(frame.source.toUtf8().constData(), frame.line,
- frame.function.toUtf8().constData())
+ QV4::CppStackFrame *frame = v4->currentStackFrame;
+ QMessageLogger(frame->source().toUtf8().constData(), frame->lineNumber(),
+ frame->function().toUtf8().constData())
.critical("%s\n%s",qPrintable(message), qPrintable(stack));
}
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void ConsoleObject::method_exception(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue ConsoleObject::method_exception(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc == 0)
+ QV4::Scope scope(b);
+ if (callData->argc() == 0)
THROW_GENERIC_ERROR("console.exception(): Missing argument");
- writeToConsole(b, scope, callData, Error, true);
-
- scope.result = QV4::Encode::undefined();
+ return writeToConsole(b, callData, Error, true);
}
@@ -1788,30 +1811,31 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext
\sa {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTranslate(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 2)
+ QV4::Scope scope(b);
+ if (callData->argc() < 2)
THROW_GENERIC_ERROR("qsTranslate() requires at least two arguments");
if (!callData->args[0].isString())
THROW_GENERIC_ERROR("qsTranslate(): first argument (context) must be a string");
if (!callData->args[1].isString())
THROW_GENERIC_ERROR("qsTranslate(): second argument (sourceText) must be a string");
- if ((callData->argc > 2) && !callData->args[2].isString())
+ if ((callData->argc() > 2) && !callData->args[2].isString())
THROW_GENERIC_ERROR("qsTranslate(): third argument (disambiguation) must be a string");
QString context = callData->args[0].toQStringNoThrow();
QString text = callData->args[1].toQStringNoThrow();
QString comment;
- if (callData->argc > 2) comment = callData->args[2].toQStringNoThrow();
+ if (callData->argc() > 2) comment = callData->args[2].toQStringNoThrow();
int i = 3;
- if (callData->argc > i && callData->args[i].isString()) {
+ if (callData->argc() > i && callData->args[i].isString()) {
qWarning("qsTranslate(): specifying the encoding as fourth argument is deprecated");
++i;
}
int n = -1;
- if (callData->argc > i)
+ if (callData->argc() > i)
n = callData->args[i].toInt32();
QString result = QCoreApplication::translate(context.toUtf8().constData(),
@@ -1819,7 +1843,7 @@ void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope,
comment.toUtf8().constData(),
n);
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
/*!
@@ -1844,12 +1868,13 @@ void GlobalExtensions::method_qsTranslate(const BuiltinFunction *, Scope &scope,
\sa {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 2)
- scope.result = QV4::Encode::undefined();
+ QV4::Scope scope(b);
+ if (callData->argc() < 2)
+ return QV4::Encode::undefined();
else
- scope.result = callData->args[1];
+ return callData->args[1].asReturnedValue();
}
/*!
@@ -1869,15 +1894,16 @@ void GlobalExtensions::method_qsTranslateNoOp(const BuiltinFunction *, Scope &sc
\sa {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTr(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1)
THROW_GENERIC_ERROR("qsTr() requires at least one argument");
if (!callData->args[0].isString())
THROW_GENERIC_ERROR("qsTr(): first argument (sourceText) must be a string");
- if ((callData->argc > 1) && !callData->args[1].isString())
+ if ((callData->argc() > 1) && !callData->args[1].isString())
THROW_GENERIC_ERROR("qsTr(): second argument (disambiguation) must be a string");
- if ((callData->argc > 2) && !callData->args[2].isNumber())
+ if ((callData->argc() > 2) && !callData->args[2].isNumber())
THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number");
QString context;
@@ -1888,10 +1914,10 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa
int length = lastDot - (lastSlash + 1);
context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString();
} else {
- ExecutionContext *parentCtx = scope.engine->currentContext;
+ CppStackFrame *frame = scope.engine->currentStackFrame;
// The first non-empty source URL in the call stack determines the translation context.
- while (!!parentCtx && context.isEmpty()) {
- if (CompiledData::CompilationUnit *unit = static_cast<CompiledData::CompilationUnit*>(parentCtx->d()->compilationUnit)) {
+ while (frame && context.isEmpty()) {
+ if (CompiledData::CompilationUnit *unit = frame->v4Function->compilationUnit) {
QString fileName = unit->fileName();
QUrl url(unit->fileName());
if (url.isValid() && url.isRelative()) {
@@ -1903,22 +1929,22 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa
}
context = QFileInfo(context).baseName();
}
- parentCtx = scope.engine->parentContext(parentCtx);
+ frame = frame->parent;
}
}
QString text = callData->args[0].toQStringNoThrow();
QString comment;
- if (callData->argc > 1)
+ if (callData->argc() > 1)
comment = callData->args[1].toQStringNoThrow();
int n = -1;
- if (callData->argc > 2)
+ if (callData->argc() > 2)
n = callData->args[2].toInt32();
QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(),
comment.toUtf8().constData(), n);
- scope.result = scope.engine->newString(result);
+ return Encode(scope.engine->newString(result));
}
/*!
@@ -1943,12 +1969,12 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa
\sa {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, CallData *callData)
{
- if (callData->argc < 1)
- scope.result = QV4::Encode::undefined();
+ if (callData->argc() < 1)
+ return QV4::Encode::undefined();
else
- scope.result = callData->args[0];
+ return callData->args[0].asReturnedValue();
}
/*!
@@ -1981,20 +2007,21 @@ void GlobalExtensions::method_qsTrNoOp(const BuiltinFunction *, Scope &scope, Ca
\sa QT_TRID_NOOP(), {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTrId(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTrId(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc < 1)
+ QV4::Scope scope(b);
+ if (callData->argc() < 1)
THROW_GENERIC_ERROR("qsTrId() requires at least one argument");
if (!callData->args[0].isString())
THROW_TYPE_ERROR_WITH_MESSAGE("qsTrId(): first argument (id) must be a string");
- if (callData->argc > 1 && !callData->args[1].isNumber())
+ if (callData->argc() > 1 && !callData->args[1].isNumber())
THROW_TYPE_ERROR_WITH_MESSAGE("qsTrId(): second argument (n) must be a number");
int n = -1;
- if (callData->argc > 1)
+ if (callData->argc() > 1)
n = callData->args[1].toInt32();
- scope.result = scope.engine->newString(qtTrId(callData->args[0].toQStringNoThrow().toUtf8().constData(), n));
+ return Encode(scope.engine->newString(qtTrId(callData->args[0].toQStringNoThrow().toUtf8().constData(), n)));
}
/*!
@@ -2013,28 +2040,29 @@ void GlobalExtensions::method_qsTrId(const BuiltinFunction *, Scope &scope, Call
\sa qsTrId(), {Internationalization and Localization with Qt Quick}
*/
-void GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, CallData *callData)
{
- if (callData->argc < 1)
- scope.result = QV4::Encode::undefined();
+ if (callData->argc() < 1)
+ return QV4::Encode::undefined();
else
- scope.result = callData->args[0];
+ return callData->args[0].asReturnedValue();
}
#endif // translation
-void GlobalExtensions::method_gc(const BuiltinFunction *, Scope &scope, CallData *)
+ReturnedValue GlobalExtensions::method_gc(const BuiltinFunction *b, CallData *)
{
- scope.engine->memoryManager->runGC();
+ b->engine()->memoryManager->runGC();
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
-void GlobalExtensions::method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData)
+ReturnedValue GlobalExtensions::method_string_arg(const BuiltinFunction *b, CallData *callData)
{
- if (callData->argc != 1)
+ QV4::Scope scope(b);
+ if (callData->argc() != 1)
THROW_GENERIC_ERROR("String.arg(): Invalid arguments");
QString value = callData->thisObject.toQString();
@@ -2070,10 +2098,10 @@ be passed on to the function invoked. Note that if redundant calls
are eliminated, then only the last set of arguments will be passed to the
function.
*/
-void QtObject::method_callLater(const BuiltinFunction *b, Scope &scope, CallData *callData)
+ReturnedValue QtObject::method_callLater(const BuiltinFunction *b, CallData *callData)
{
- QV8Engine *v8engine = scope.engine->v8Engine;
- v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, scope, callData);
+ QV8Engine *v8engine = b->engine()->v8Engine;
+ return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(b, callData);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index 21613b7c10..7d61aa0ada 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -93,45 +93,45 @@ struct QtObject : Object
static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static void method_isQtObject(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_rgba(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_hsla(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_hsva(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_colorEqual(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_font(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_rect(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_point(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_size(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_vector2d(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_vector3d(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_vector4d(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_quaternion(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_matrix4x4(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_lighter(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_darker(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_tint(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_formatDate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_formatTime(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_formatDateTime(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_openUrlExternally(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_fontFamilies(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_md5(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_btoa(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_atob(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_quit(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_exit(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_resolvedUrl(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_createQmlObject(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_createComponent(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_locale(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_binding(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_get_platform(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_application(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_inputMethod(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_get_styleHints(const BuiltinFunction *, Scope &scope, CallData *callData);
-
- static void method_callLater(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_isQtObject(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_rgba(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_hsla(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_hsva(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_colorEqual(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_font(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_rect(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_point(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_size(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_vector2d(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_vector3d(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_vector4d(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_quaternion(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_matrix4x4(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_lighter(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_darker(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_tint(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_formatDate(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_formatTime(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_formatDateTime(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_openUrlExternally(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_fontFamilies(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_md5(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_btoa(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_atob(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_quit(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_exit(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_resolvedUrl(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_createQmlObject(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_createComponent(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_locale(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_binding(const BuiltinFunction *, CallData *callData);
+
+ static ReturnedValue method_get_platform(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_get_application(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_get_inputMethod(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_get_styleHints(const BuiltinFunction *, CallData *callData);
+
+ static ReturnedValue method_callLater(const BuiltinFunction *, CallData *callData);
private:
void addAll();
@@ -142,18 +142,18 @@ struct ConsoleObject : Object
{
V4_OBJECT2(ConsoleObject, Object)
- static void method_error(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_log(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_info(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_profile(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_profileEnd(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_time(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_timeEnd(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_count(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_trace(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_warn(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_assert(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_exception(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_error(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_log(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_info(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_profile(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_profileEnd(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_time(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_timeEnd(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_count(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_trace(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_warn(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_assert(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_exception(const BuiltinFunction *, CallData *callData);
};
@@ -161,17 +161,17 @@ struct Q_QML_PRIVATE_EXPORT GlobalExtensions {
static void init(Object *globalObject, QJSEngine::Extensions extensions);
#if QT_CONFIG(translation)
- static void method_qsTranslate(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_qsTranslateNoOp(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_qsTr(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_qsTrNoOp(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_qsTrId(const BuiltinFunction *, Scope &scope, CallData *callData);
- static void method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_qsTranslate(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_qsTranslateNoOp(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_qsTr(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_qsTrNoOp(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_qsTrId(const BuiltinFunction *, CallData *callData);
+ static ReturnedValue method_qsTrIdNoOp(const BuiltinFunction *, CallData *callData);
#endif
- static void method_gc(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_gc(const BuiltinFunction *, CallData *callData);
// on String:prototype
- static void method_string_arg(const BuiltinFunction *, Scope &scope, CallData *callData);
+ static ReturnedValue method_string_arg(const BuiltinFunction *, CallData *callData);
};
diff --git a/src/qml/qml/v8/qv4domerrors_p.h b/src/qml/qml/v8/qv4domerrors_p.h
index a9bdbe01ae..06a70a13e9 100644
--- a/src/qml/qml/v8/qv4domerrors_p.h
+++ b/src/qml/qml/v8/qv4domerrors_p.h
@@ -78,8 +78,7 @@ QT_BEGIN_NAMESPACE
QV4::ScopedValue v(scope, scope.engine->newString(QStringLiteral(string))); \
QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \
ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))), QV4::ScopedValue(scope, QV4::Primitive::fromInt32(error))); \
- scope.result = scope.engine->throwError(ex); \
- return; \
+ return scope.engine->throwError(ex); \
}
namespace QV4 {
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index 3bd3517968..a430fba0e6 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -116,8 +116,8 @@ namespace QV4 {
class QQmlV4Function
{
public:
- int length() const { return callData->argc; }
- QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); }
+ int length() const { return callData->argc(); }
+ QV4::ReturnedValue operator[](int idx) const { return (idx < callData->argc() ? callData->args[idx].asReturnedValue() : QV4::Encode::undefined()); }
void setReturnValue(QV4::ReturnedValue rv) { *retVal = rv; }
QV4::ExecutionEngine *v4engine() const { return e; }
private:
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 4d2a9746c3..967f89971d 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -96,17 +96,16 @@ struct DelegateModelGroupFunction : QV4::FunctionObject
return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code);
}
- static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData)
+ static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc)
{
+ QV4::Scope scope(that->engine());
QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that));
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject);
- if (!o) {
- scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- return;
- }
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject);
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- QV4::ScopedValue v(scope, callData->argument(0));
- scope.result = f->d()->code(o->d()->item, f->d()->flag, v);
+ QV4::ScopedValue v(scope, argc ? argv[0] : Primitive::undefinedValue());
+ return f->d()->code(o->d()->item, f->d()->flag, v);
}
};
@@ -1807,26 +1806,24 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const
return groupFlags;
}
-void QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
- if (!o) {
- scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- return;
- }
+ if (!o)
+ return b->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
if (!o->d()->item->metaType->model)
RETURN_UNDEFINED();
- scope.result = o->d()->item->get();
+ return o->d()->item->get();
}
-void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
- if (!o) {
- scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- return;
- }
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
QStringList groups;
for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) {
@@ -1834,18 +1831,17 @@ void QQmlDelegateModelItem::get_groups(const QV4::BuiltinFunction *, QV4::Scope
groups.append(o->d()->item->metaType->groupNames.at(i - 1));
}
- scope.result = scope.engine->fromVariant(groups);
+ return scope.engine->fromVariant(groups);
}
-void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
- if (!o) {
- scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- return;
- }
+ if (!o)
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- if (!callData->argc)
+ if (!callData->argc())
THROW_TYPE_ERROR();
if (!o->d()->item->metaType->model)
@@ -1856,7 +1852,7 @@ void QQmlDelegateModelItem::set_groups(const QV4::BuiltinFunction *, QV4::Scope
const int cacheIndex = model->m_cache.indexOf(o->d()->item);
Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
model->setGroups(it, 1, Compositor::Cache, groupFlags);
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &)
@@ -3226,25 +3222,28 @@ struct QQmlDelegateModelGroupChange : QV4::Object
return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>();
}
- static void method_get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) {
+ static QV4::ReturnedValue method_get_index(const QV4::BuiltinFunction *b, QV4::CallData *callData) {
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>());
if (!that)
THROW_TYPE_ERROR();
- scope.result = QV4::Encode(that->d()->change.index);
+ return QV4::Encode(that->d()->change.index);
}
- static void method_get_count(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) {
+ static QV4::ReturnedValue method_get_count(const QV4::BuiltinFunction *b, QV4::CallData *callData) {
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>());
if (!that)
THROW_TYPE_ERROR();
- scope.result = QV4::Encode(that->d()->change.count);
+ return QV4::Encode(that->d()->change.count);
}
- static void method_get_moveId(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) {
+ static QV4::ReturnedValue method_get_moveId(const QV4::BuiltinFunction *b, QV4::CallData *callData) {
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, callData->thisObject.as<QQmlDelegateModelGroupChange>());
if (!that)
THROW_TYPE_ERROR();
if (that->d()->change.moveId < 0)
RETURN_UNDEFINED();
- scope.result = QV4::Encode(that->d()->change.moveId);
+ return QV4::Encode(that->d()->change.moveId);
}
};
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 3759fe8667..e0416e93ee 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -126,9 +126,9 @@ public:
virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); }
virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; }
- static void get_model(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void get_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void set_groups(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue get_model(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue get_groups(const QV4::BuiltinFunction *, QV4::CallData *callData);
+ static QV4::ReturnedValue set_groups(const QV4::BuiltinFunction *, QV4::CallData *callData);
static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &);
static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg);
static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg);
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index c8f8a0fc70..e32e0c75f3 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -2591,14 +2591,10 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp
QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr));
QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
- // ### we need the inner function declaration (at this point the function has been wrapped)
- const unsigned int parameterCount = function->formalParameterCount();
- QV4::ScopedCallData callData(scope, parameterCount);
- callData->thisObject = v4->globalObject;
- function->call(scope, callData);
+ QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0);
QJSValue v;
- QJSValuePrivate::setValue(&v, v4, scope.result);
+ QJSValuePrivate::setValue(&v, v4, result);
value.setValue<QJSValue>(v);
} else {
QByteArray script = scriptStr.toUtf8();
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index 6159355afc..346f2e6a61 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -65,6 +65,7 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4script_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
QT_BEGIN_NAMESPACE
@@ -185,7 +186,7 @@ public:
int m_nextId;
- static void method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_sendMessage(const QV4::BuiltinFunction *, QV4::CallData *callData);
signals:
void stopThread();
@@ -239,19 +240,18 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
QV4::Scope scope(m_v4Engine);
QV4::ExecutionContext *globalContext = scope.engine->rootContext();
- onmessage.set(scope.engine, QV4::Script(globalContext, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
+ onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
Q_ASSERT(!scope.engine->hasException);
- QV4::Script createsendscript(globalContext, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
+ QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run());
Q_ASSERT(!scope.engine->hasException);
QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
QV4::ScopedValue function(scope, QV4::BuiltinFunction::create(globalContext, name,
- QQuickWorkerScriptEnginePrivate::method_sendMessage));
- QV4::ScopedCallData callData(scope, 1);
- callData->args[0] = function;
- callData->thisObject = global();
- createsendconstructor->call(scope, callData);
- createsend.set(scope.engine, scope.result.asReturnedValue());
+ QQuickWorkerScriptEnginePrivate::method_sendMessage));
+ QV4::JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = function;
+ *jsCallData->thisObject = global();
+ createsend.set(scope.engine, createsendconstructor->call(jsCallData));
}
// Requires handle and context scope
@@ -264,13 +264,14 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i
QV4::Scope scope(v4);
QV4::ScopedFunctionObject f(scope, createsend.value());
- QV4::ScopedCallData callData(scope, 1);
- callData->args[0] = QV4::Primitive::fromInt32(id);
- callData->thisObject = global();
- f->call(scope, callData);
+ QV4::ScopedValue v(scope);
+ QV4::JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = QV4::Primitive::fromInt32(id);
+ *jsCallData->thisObject = global();
+ v = f->call(jsCallData);
if (scope.hasException())
- scope.result = scope.engine->catchException();
- return scope.result.asReturnedValue();
+ v = scope.engine->catchException();
+ return v->asReturnedValue();
}
#if QT_CONFIG(qml_network)
@@ -292,11 +293,13 @@ QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *eng
{
}
-void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunction *b,
+ QV4::CallData *callData)
{
+ QV4::Scope scope(b);
WorkerEngine *engine = (WorkerEngine*)scope.engine->v8Engine;
- int id = callData->argc > 1 ? callData->args[1].toInt32() : 0;
+ int id = callData->argc() > 1 ? callData->args[1].toInt32() : 0;
QV4::ScopedValue v(scope, callData->argument(2));
QByteArray data = QV4::Serialize::serialize(v, scope.engine);
@@ -306,7 +309,7 @@ void QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::BuiltinFunct
if (script && script->owner)
QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data));
- scope.result = QV4::Encode::undefined();
+ return QV4::Encode::undefined();
}
QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script)
@@ -363,11 +366,11 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d
QV4::Scoped<QV4::QmlContext> qmlContext(scope, script->qmlContext.value());
Q_ASSERT(!!qmlContext);
- QV4::ScopedCallData callData(scope, 2);
- callData->thisObject = workerEngine->global();
- callData->args[0] = qmlContext->d()->qml; // ###
- callData->args[1] = value;
- f->call(scope, callData);
+ QV4::JSCallData jsCallData(scope, 2);
+ *jsCallData->thisObject = workerEngine->global();
+ jsCallData->args[0] = qmlContext->d()->qml(); // ###
+ jsCallData->args[1] = value;
+ f->call(jsCallData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
reportScriptException(script, error);
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index 11fed281b7..d5b38f6d3e 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -61,8 +61,9 @@ public:
V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
-static void get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+static QV4::ReturnedValue get_index(const QV4::BuiltinFunction *f, QV4::CallData *callData)
{
+ QV4::Scope scope(f);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
@@ -105,8 +106,8 @@ public:
void setValue(const QString &role, const QVariant &value) override;
bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
- static QV4::ReturnedValue get_property(QV4::CallContext *ctx, uint propertyId);
- static QV4::ReturnedValue set_property(QV4::CallContext *ctx, uint propertyId);
+ static QV4::ReturnedValue get_property(const QV4::BuiltinFunction *, QV4::CallData *);
+ static QV4::ReturnedValue set_property(const QV4::BuiltinFunction *, QV4::CallData *);
VDMModelDelegateDataType *type;
QVector<QVariant> cachedData;
@@ -194,8 +195,9 @@ public:
dataType->watchedRoles += newRoles;
}
- static void get_hasModelChildren(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ static QV4::ReturnedValue get_hasModelChildren(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
@@ -341,12 +343,14 @@ bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx)
}
}
-QV4::ReturnedValue QQmlDMCachedModelData::get_property(QV4::CallContext *ctx, uint propertyId)
+QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+
+ uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
if (o->d()->item->index == -1) {
@@ -361,23 +365,25 @@ QV4::ReturnedValue QQmlDMCachedModelData::get_property(QV4::CallContext *ctx, ui
return QV4::Encode::undefined();
}
-QV4::ReturnedValue QQmlDMCachedModelData::set_property(QV4::CallContext *ctx, uint propertyId)
+QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scope scope(b);
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- if (!ctx->argc())
- return ctx->engine()->throwTypeError();
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ if (!callData->argc())
+ return scope.engine->throwTypeError();
+
+ uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
if (o->d()->item->index == -1) {
QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
if (!modelData->cachedData.isEmpty()) {
if (modelData->cachedData.count() > 1) {
- modelData->cachedData[propertyId] = scope.engine->toVariant(ctx->args()[0], QVariant::Invalid);
+ modelData->cachedData[propertyId] = scope.engine->toVariant(callData->args[0], QVariant::Invalid);
QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, 0);
} else if (modelData->cachedData.count() == 1) {
- modelData->cachedData[0] = scope.engine->toVariant(ctx->args()[0], QVariant::Invalid);
+ modelData->cachedData[0] = scope.engine->toVariant(callData->args[0], QVariant::Invalid);
QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, 0);
QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, 0);
}
@@ -580,25 +586,27 @@ public:
}
}
- static void get_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ static QV4::ReturnedValue get_modelData(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
+ QV4::ExecutionEngine *v4 = b->engine();
+ QQmlDelegateModelItemObject *o = callData->thisObject.as<QQmlDelegateModelItemObject>();
if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
+ return v4->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- RETURN_RESULT(scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData));
+ return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
}
- static void set_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+ static QV4::ReturnedValue set_modelData(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
+ QV4::ExecutionEngine *v4 = b->engine();
+ QQmlDelegateModelItemObject *o = callData->thisObject.as<QQmlDelegateModelItemObject>();
if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
- if (!callData->argc)
- RETURN_RESULT(scope.engine->throwTypeError());
+ return v4->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ if (!callData->argc())
+ return v4->throwTypeError();
- static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(callData->args[0], QVariant::Invalid));
- RETURN_RESULT(QV4::Encode::undefined());
+ static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(v4->toVariant(callData->args[0], QVariant::Invalid));
+ return QV4::Encode::undefined();
}
QV4::ReturnedValue get() override
diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro
index a21988b915..5ee97776b8 100644
--- a/src/qmldevtools/qmldevtools.pro
+++ b/src/qmldevtools/qmldevtools.pro
@@ -16,6 +16,5 @@ include(../qml/parser/parser.pri)
include(../qml/jsruntime/jsruntime.pri)
include(../qml/compiler/compiler.pri)
include(../qml/memory/memory.pri)
-include(../qml/jit/jit.pri)
load(qt_module)
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index 90d11e9734..8a2282f5c6 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -57,6 +57,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/QUrl>
#include <QtCore/QDir>
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index b492842ad4..57936d8cec 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -57,6 +57,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
@@ -728,13 +729,14 @@ void QQuickCanvasItem::updatePolish()
QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(qmlEngine(this));
QV4::Scope scope(v4);
- QV4::ScopedCallData callData(scope, 1);
- callData->thisObject = QV4::QObjectWrapper::wrap(v4, this);
+ QV4::ScopedFunctionObject function(scope);
+ QV4::JSCallData jsCall(scope, 1);
+ *jsCall->thisObject = QV4::QObjectWrapper::wrap(v4, this);
for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
- QV4::ScopedFunctionObject f(scope, it.value().value());
- callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
- f->call(scope, callData);
+ function = it.value().value();
+ jsCall->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
+ function->call(jsCall);
}
}
else {
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 37e574a884..49b2bf5838 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -529,6 +529,11 @@ struct QQuickJSContext2DPixelData : Object {
struct QQuickJSContext2DImageData : Object {
void init();
+ static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) {
+ static_cast<QQuickJSContext2DImageData *>(that)->pixelData.mark(markStack);
+ Object::markObjects(that, markStack);
+ }
+
QV4::Value pixelData;
};
@@ -539,46 +544,46 @@ struct QQuickJSContext2D : public QV4::Object
{
V4_OBJECT2(QQuickJSContext2D, QV4::Object)
- static void method_get_globalAlpha(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_globalAlpha(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_globalCompositeOperation(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_globalCompositeOperation(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_fillStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_fillStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_lineCap(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_lineCap(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_lineJoin(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_lineJoin(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
-
- static void method_get_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_shadowColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_shadowColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_globalAlpha(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_globalAlpha(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_globalCompositeOperation(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_globalCompositeOperation(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_fillStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_fillStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_fillRule(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_fillRule(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_strokeStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_strokeStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+
+ static QV4::ReturnedValue method_get_lineCap(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_lineCap(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_lineJoin(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_lineJoin(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_lineWidth(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_lineWidth(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_miterLimit(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_miterLimit(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+
+ static QV4::ReturnedValue method_get_shadowBlur(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_shadowBlur(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_shadowColor(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_shadowColor(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_shadowOffsetX(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_shadowOffsetX(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_shadowOffsetY(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_shadowOffsetY(const QV4::BuiltinFunction *b, QV4::CallData *callData);
// should these two be on the proto?
#if QT_CONFIG(quick_path)
- static void method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_path(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_path(const QV4::BuiltinFunction *b, QV4::CallData *callData);
#endif
- static void method_get_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_textBaseline(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_set_textBaseline(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_font(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_font(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_textAlign(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_textAlign(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_textBaseline(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_set_textBaseline(const QV4::BuiltinFunction *b, QV4::CallData *callData);
};
DEFINE_OBJECT_VTABLE(QQuickJSContext2D);
@@ -641,50 +646,50 @@ public:
return o->d();
}
- static void method_get_canvas(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_restore(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_reset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_save(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_rotate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_scale(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_translate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_setTransform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_transform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_resetTransform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_shear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_createLinearGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_createRadialGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_createConicalGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_createPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_clearRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fillRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_strokeRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_arc(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_beginPath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_bezierCurveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_clip(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_closePath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fill(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_quadraticCurveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_rect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_roundedRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_ellipse(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_text(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_stroke(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_isPointInPath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_drawFocusRing(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_setCaretSelectionRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_caretBlinkRate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_fillText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_strokeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_measureText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_drawImage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_createImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_getImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_putImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_canvas(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_restore(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_reset(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_save(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_rotate(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_scale(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_translate(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_setTransform(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_transform(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_resetTransform(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_shear(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_createLinearGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_createRadialGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_createConicalGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_createPattern(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_clearRect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fillRect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_strokeRect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_arc(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_arcTo(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_beginPath(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_bezierCurveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_clip(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_closePath(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fill(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_lineTo(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_moveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_quadraticCurveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_rect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_roundedRect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_ellipse(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_text(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_stroke(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_isPointInPath(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_drawFocusRing(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_setCaretSelectionRect(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_caretBlinkRate(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_fillText(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_strokeText(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_measureText(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_drawImage(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_createImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_getImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_putImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData);
};
@@ -696,7 +701,7 @@ struct QQuickContext2DStyle : public QV4::Object
V4_OBJECT2(QQuickContext2DStyle, QV4::Object)
V4_NEEDS_DESTROY
- static void gradient_proto_addColorStop(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue gradient_proto_addColorStop(const QV4::BuiltinFunction *b, QV4::CallData *callData);
};
@@ -906,7 +911,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object
static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty);
static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
- static void proto_get_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue proto_get_length(const QV4::BuiltinFunction *b, QV4::CallData *callData);
};
void QV4::Heap::QQuickJSContext2DPixelData::init()
@@ -924,14 +929,10 @@ struct QQuickJSContext2DImageData : public QV4::Object
{
V4_OBJECT2(QQuickJSContext2DImageData, QV4::Object)
- static void method_get_width(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_height(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void method_get_data(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_width(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_height(const QV4::BuiltinFunction *b, QV4::CallData *callData);
+ static QV4::ReturnedValue method_get_data(const QV4::BuiltinFunction *b, QV4::CallData *callData);
- static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) {
- static_cast<QQuickJSContext2DImageData::Data *>(that)->pixelData.mark(markStack);
- QV4::Object::markObjects(that, markStack);
- }
};
void QV4::Heap::QQuickJSContext2DImageData::init()
@@ -978,12 +979,13 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
This property is read only.
*/
-void QQuickJSContext2DPrototype::method_get_canvas(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_get_canvas(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- scope.result = QV4::QObjectWrapper::wrap(scope.engine, r->d()->context->canvas());
+ RETURN_RESULT(QV4::QObjectWrapper::wrap(scope.engine, r->d()->context->canvas()));
}
/*!
@@ -992,27 +994,29 @@ void QQuickJSContext2DPrototype::method_get_canvas(const QV4::BuiltinFunction *,
\sa save()
*/
-void QQuickJSContext2DPrototype::method_restore(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_restore(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
r->d()->context->popState();
- scope.result = callData->thisObject.asReturnedValue();
+ RETURN_RESULT(callData->thisObject.asReturnedValue());
}
/*!
\qmlmethod object QtQuick::Context2D::reset()
Resets the context state and properties to the default values.
*/
-void QQuickJSContext2DPrototype::method_reset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_reset(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
r->d()->context->reset();
- scope.result = callData->thisObject.asReturnedValue();
+ RETURN_RESULT(callData->thisObject.asReturnedValue());
}
/*!
@@ -1045,14 +1049,15 @@ void QQuickJSContext2DPrototype::method_reset(const QV4::BuiltinFunction *, QV4:
The current path is NOT part of the drawing state. The path can be reset by
invoking the beginPath() method.
*/
-void QQuickJSContext2DPrototype::method_save(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_save(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
r->d()->context->pushState();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
// transformations
@@ -1073,14 +1078,15 @@ void QQuickJSContext2DPrototype::method_save(const QV4::BuiltinFunction *, QV4::
where the \a angle of rotation is in radians.
*/
-void QQuickJSContext2DPrototype::method_rotate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_rotate(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 1)
+ if (callData->argc() >= 1)
r->d()->context->rotate(callData->args[0].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -1100,15 +1106,16 @@ void QQuickJSContext2DPrototype::method_rotate(const QV4::BuiltinFunction *, QV4
\image qml-item-canvas-scale.png
*/
-void QQuickJSContext2DPrototype::method_scale(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_scale(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 2)
+ if (callData->argc() >= 2)
r->d()->context->scale(callData->args[0].toNumber(), callData->args[1].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1146,13 +1153,14 @@ void QQuickJSContext2DPrototype::method_scale(const QV4::BuiltinFunction *, QV4:
\sa transform()
*/
-void QQuickJSContext2DPrototype::method_setTransform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_setTransform(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 6)
+ if (callData->argc() >= 6)
r->d()->context->setTransform( callData->args[0].toNumber()
, callData->args[1].toNumber()
, callData->args[2].toNumber()
@@ -1160,7 +1168,7 @@ void QQuickJSContext2DPrototype::method_setTransform(const QV4::BuiltinFunction
, callData->args[4].toNumber()
, callData->args[5].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1175,12 +1183,13 @@ void QQuickJSContext2DPrototype::method_setTransform(const QV4::BuiltinFunction
\sa setTransform()
*/
-void QQuickJSContext2DPrototype::method_transform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_transform(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 6)
+ if (callData->argc() >= 6)
r->d()->context->transform( callData->args[0].toNumber()
, callData->args[1].toNumber()
, callData->args[2].toNumber()
@@ -1188,7 +1197,7 @@ void QQuickJSContext2DPrototype::method_transform(const QV4::BuiltinFunction *,
, callData->args[4].toNumber()
, callData->args[5].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1201,14 +1210,15 @@ void QQuickJSContext2DPrototype::method_transform(const QV4::BuiltinFunction *,
Translating the origin enables you to draw patterns of different objects on the canvas
without having to measure the coordinates manually for each shape.
*/
-void QQuickJSContext2DPrototype::method_translate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_translate(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 2)
+ if (callData->argc() >= 2)
r->d()->context->translate(callData->args[0].toNumber(), callData->args[1].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1221,14 +1231,15 @@ void QQuickJSContext2DPrototype::method_translate(const QV4::BuiltinFunction *,
\sa transform(), setTransform(), reset()
*/
-void QQuickJSContext2DPrototype::method_resetTransform(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_resetTransform(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
r->d()->context->setTransform(1, 0, 0, 1, 0, 0);
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1239,15 +1250,16 @@ void QQuickJSContext2DPrototype::method_resetTransform(const QV4::BuiltinFunctio
Shears the transformation matrix by \a sh in the horizontal direction and
\a sv in the vertical direction.
*/
-void QQuickJSContext2DPrototype::method_shear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_shear(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 2)
+ if (callData->argc() >= 2)
r->d()->context->shear(callData->args[0].toNumber(), callData->args[1].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
// compositing
@@ -1259,30 +1271,32 @@ void QQuickJSContext2DPrototype::method_shear(const QV4::BuiltinFunction *, QV4:
The value must be in the range from \c 0.0 (fully transparent) to \c 1.0 (fully opaque).
The default value is \c 1.0.
*/
-void QQuickJSContext2D::method_get_globalAlpha(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_globalAlpha(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- scope.result = QV4::Encode(r->d()->context->state.globalAlpha);
+ RETURN_RESULT(QV4::Encode(r->d()->context->state.globalAlpha));
}
-void QQuickJSContext2D::method_set_globalAlpha(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_globalAlpha(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- double globalAlpha = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ double globalAlpha = callData->argc() ? callData->args[0].toNumber() : qt_qnan();
- scope.result = QV4::Encode::undefined();
if (!qt_is_finite(globalAlpha))
- return;
+ RETURN_UNDEFINED();
if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->d()->context->state.globalAlpha != globalAlpha) {
r->d()->context->state.globalAlpha = globalAlpha;
r->d()->context->buffer()->setGlobalAlpha(r->d()->context->state.globalAlpha);
}
+ RETURN_UNDEFINED();
}
/*!
@@ -1311,33 +1325,35 @@ void QQuickJSContext2D::method_set_globalAlpha(const QV4::BuiltinFunction *, QV4
extension composition modes are provided as "vendorName-operationName" syntax, for example: QPainter::CompositionMode_Exclusion is provided as
"qt-exclusion".
*/
-void QQuickJSContext2D::method_get_globalCompositeOperation(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_globalCompositeOperation(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- scope.result = scope.engine->newString(qt_composite_mode_to_string(r->d()->context->state.globalCompositeOperation));
+ RETURN_RESULT(scope.engine->newString(qt_composite_mode_to_string(r->d()->context->state.globalCompositeOperation)));
}
-void QQuickJSContext2D::method_set_globalCompositeOperation(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_globalCompositeOperation(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- if (!callData->argc)
+ if (!callData->argc())
THROW_TYPE_ERROR();
- scope.result = QV4::Encode::undefined();
-
QString mode = callData->args[0].toQString();
QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
if (cm == QPainter::CompositionMode_SourceOver && mode != QLatin1String("source-over"))
- return;
+ RETURN_UNDEFINED();
if (cm != r->d()->context->state.globalCompositeOperation) {
r->d()->context->state.globalCompositeOperation = cm;
r->d()->context->buffer()->setGlobalCompositeOperation(cm);
}
+
+ RETURN_UNDEFINED();
}
// colors and styles
@@ -1363,8 +1379,9 @@ void QQuickJSContext2D::method_set_globalCompositeOperation(const QV4::BuiltinFu
\sa createPattern()
\sa strokeStyle
*/
-void QQuickJSContext2D::method_get_fillStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_fillStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
@@ -1380,11 +1397,12 @@ void QQuickJSContext2D::method_get_fillStyle(const QV4::BuiltinFunction *, QV4::
QString str = QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString);
RETURN_RESULT(scope.engine->newString(str));
}
- scope.result = r->d()->context->m_fillStyle.value();
+ RETURN_RESULT(r->d()->context->m_fillStyle.value());
}
-void QQuickJSContext2D::method_set_fillStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
@@ -1414,7 +1432,7 @@ void QQuickJSContext2D::method_set_fillStyle(const QV4::BuiltinFunction *, QV4::
r->d()->context->m_fillStyle.set(scope.engine, value);
}
}
- scope.result = QV4::Encode::undefined();
+ RETURN_UNDEFINED();
}
/*!
\qmlproperty enumeration QtQuick::Context2D::fillRule
@@ -1428,16 +1446,18 @@ void QQuickJSContext2D::method_set_fillStyle(const QV4::BuiltinFunction *, QV4::
\sa fillStyle
*/
-void QQuickJSContext2D::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_fillRule(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- scope.result = scope.engine->fromVariant(r->d()->context->state.fillRule);
+ RETURN_RESULT(scope.engine->fromVariant(r->d()->context->state.fillRule));
}
-void QQuickJSContext2D::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
@@ -1453,7 +1473,7 @@ void QQuickJSContext2D::method_set_fillRule(const QV4::BuiltinFunction *, QV4::S
//error
}
r->d()->context->m_path.setFillRule(r->d()->context->state.fillRule);
- scope.result = QV4::Encode::undefined();
+ RETURN_UNDEFINED();
}
/*!
\qmlproperty variant QtQuick::Context2D::strokeStyle
@@ -1468,8 +1488,9 @@ void QQuickJSContext2D::method_set_fillRule(const QV4::BuiltinFunction *, QV4::S
\sa createPattern()
\sa fillStyle
*/
-void QQuickJSContext2D::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_strokeStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
@@ -1485,11 +1506,12 @@ void QQuickJSContext2D::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4
QString str = QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString);
RETURN_RESULT(scope.engine->newString(str));
}
- scope.result = r->d()->context->m_strokeStyle.value();
+ RETURN_RESULT(r->d()->context->m_strokeStyle.value());
}
-void QQuickJSContext2D::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
@@ -1520,7 +1542,7 @@ void QQuickJSContext2D::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4
r->d()->context->m_strokeStyle.set(scope.engine, value);
}
}
- scope.result = QV4::Encode::undefined();
+ RETURN_UNDEFINED();
}
/*!
@@ -1540,12 +1562,13 @@ void QQuickJSContext2D::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4
\sa strokeStyle
*/
-void QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 4) {
+ if (callData->argc() >= 4) {
qreal x0 = callData->args[0].toNumber();
qreal y0 = callData->args[1].toNumber();
qreal x1 = callData->args[2].toNumber();
@@ -1563,10 +1586,10 @@ void QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::BuiltinF
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
*gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
- RETURN_RESULT(gradient);
+ RETURN_RESULT(*gradient);
}
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1583,12 +1606,13 @@ void QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::BuiltinF
\sa strokeStyle
*/
-void QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 6) {
+ if (callData->argc() >= 6) {
qreal x0 = callData->args[0].toNumber();
qreal y0 = callData->args[1].toNumber();
qreal r0 = callData->args[2].toNumber();
@@ -1614,10 +1638,10 @@ void QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::BuiltinF
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
*gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
- RETURN_RESULT(gradient);
+ RETURN_RESULT(*gradient);
}
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -1634,12 +1658,13 @@ void QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::BuiltinF
\sa strokeStyle
*/
-void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 3) {
+ if (callData->argc() >= 3) {
qreal x = callData->args[0].toNumber();
qreal y = callData->args[1].toNumber();
qreal angle = qRadiansToDegrees(callData->args[2].toNumber());
@@ -1657,10 +1682,10 @@ void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::Builtin
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
*gradient->d()->brush = QConicalGradient(x, y, angle);
- RETURN_RESULT(gradient);
+ RETURN_RESULT(*gradient);
}
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -1706,12 +1731,13 @@ void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::Builtin
\sa strokeStyle
\sa fillStyle
*/
-void QQuickJSContext2DPrototype::method_createPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 2) {
+ if (callData->argc() >= 2) {
QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QColor color = scope.engine->toVariant(callData->args[0], qMetaTypeId<QColor>()).value<QColor>();
@@ -1758,10 +1784,10 @@ void QQuickJSContext2DPrototype::method_createPattern(const QV4::BuiltinFunction
}
}
- RETURN_RESULT(pattern);
+ RETURN_RESULT(*pattern);
}
- scope.result = QV4::Encode::undefined();
+ RETURN_UNDEFINED();
}
// line styles
@@ -1776,8 +1802,9 @@ void QQuickJSContext2DPrototype::method_createPattern(const QV4::BuiltinFunction
\endlist
Other values are ignored.
*/
-void QQuickJSContext2D::method_get_lineCap(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineCap(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
@@ -1793,8 +1820,10 @@ void QQuickJSContext2D::method_get_lineCap(const QV4::BuiltinFunction *, QV4::Sc
RETURN_RESULT(scope.engine->newString(QStringLiteral("butt")));
}
-void QQuickJSContext2D::method_set_lineCap(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
@@ -1830,8 +1859,9 @@ void QQuickJSContext2D::method_set_lineCap(const QV4::BuiltinFunction *, QV4::Sc
\endlist
Other values are ignored.
*/
-void QQuickJSContext2D::method_get_lineJoin(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineJoin(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
@@ -1847,12 +1877,13 @@ void QQuickJSContext2D::method_get_lineJoin(const QV4::BuiltinFunction *, QV4::S
RETURN_RESULT(scope.engine->newString(QStringLiteral("miter")));
}
-void QQuickJSContext2D::method_set_lineJoin(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineJoin(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- if (!callData->argc)
+ if (!callData->argc())
THROW_TYPE_ERROR();
QString lineJoin = callData->args[0].toQString();
@@ -1877,20 +1908,22 @@ void QQuickJSContext2D::method_set_lineJoin(const QV4::BuiltinFunction *, QV4::S
\qmlproperty real QtQuick::Context2D::lineWidth
Holds the current line width. Values that are not finite values greater than zero are ignored.
*/
-void QQuickJSContext2D::method_get_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineWidth(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(QV4::Encode(r->d()->context->state.lineWidth));
}
-void QQuickJSContext2D::method_set_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineWidth(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- qreal w = callData->argc ? callData->args[0].toNumber() : -1;
+ qreal w = callData->argc() ? callData->args[0].toNumber() : -1;
if (w > 0 && qt_is_finite(w) && w != r->d()->context->state.lineWidth) {
r->d()->context->state.lineWidth = w;
@@ -1904,20 +1937,22 @@ void QQuickJSContext2D::method_set_lineWidth(const QV4::BuiltinFunction *, QV4::
Holds the current miter limit ratio.
The default miter limit value is 10.0.
*/
-void QQuickJSContext2D::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_miterLimit(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(QV4::Encode(r->d()->context->state.miterLimit));
}
-void QQuickJSContext2D::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- qreal ml = callData->argc ? callData->args[0].toNumber() : -1;
+ qreal ml = callData->argc() ? callData->args[0].toNumber() : -1;
if (ml > 0 && qt_is_finite(ml) && ml != r->d()->context->state.miterLimit) {
r->d()->context->state.miterLimit = ml;
@@ -1931,20 +1966,22 @@ void QQuickJSContext2D::method_set_miterLimit(const QV4::BuiltinFunction *, QV4:
\qmlproperty real QtQuick::Context2D::shadowBlur
Holds the current level of blur applied to shadows
*/
-void QQuickJSContext2D::method_get_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_shadowBlur(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowBlur));
}
-void QQuickJSContext2D::method_set_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_shadowBlur(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- qreal blur = callData->argc ? callData->args[0].toNumber() : -1;
+ qreal blur = callData->argc() ? callData->args[0].toNumber() : -1;
if (blur > 0 && qt_is_finite(blur) && blur != r->d()->context->state.shadowBlur) {
r->d()->context->state.shadowBlur = blur;
@@ -1957,21 +1994,23 @@ void QQuickJSContext2D::method_set_shadowBlur(const QV4::BuiltinFunction *, QV4:
\qmlproperty string QtQuick::Context2D::shadowColor
Holds the current shadow color.
*/
-void QQuickJSContext2D::method_get_shadowColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_shadowColor(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(scope.engine->newString(r->d()->context->state.shadowColor.name()));
}
-void QQuickJSContext2D::method_set_shadowColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_shadowColor(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
QColor color;
- if (callData->argc)
+ if (callData->argc())
color = qt_color_from_string(callData->args[0]);
if (color.isValid() && color != r->d()->context->state.shadowColor) {
@@ -1988,20 +2027,22 @@ void QQuickJSContext2D::method_set_shadowColor(const QV4::BuiltinFunction *, QV4
\sa shadowOffsetY
*/
-void QQuickJSContext2D::method_get_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetX(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetX));
}
-void QQuickJSContext2D::method_set_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetX(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- qreal offsetX = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ qreal offsetX = callData->argc() ? callData->args[0].toNumber() : qt_qnan();
if (qt_is_finite(offsetX) && offsetX != r->d()->context->state.shadowOffsetX) {
r->d()->context->state.shadowOffsetX = offsetX;
r->d()->context->buffer()->setShadowOffsetX(offsetX);
@@ -2014,20 +2055,22 @@ void QQuickJSContext2D::method_set_shadowOffsetX(const QV4::BuiltinFunction *, Q
\sa shadowOffsetX
*/
-void QQuickJSContext2D::method_get_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetY(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetY));
}
-void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
- qreal offsetY = callData->argc ? callData->args[0].toNumber() : qt_qnan();
+ qreal offsetY = callData->argc() ? callData->args[0].toNumber() : qt_qnan();
if (qt_is_finite(offsetY) && offsetY != r->d()->context->state.shadowOffsetY) {
r->d()->context->state.shadowOffsetY = offsetY;
r->d()->context->buffer()->setShadowOffsetY(offsetY);
@@ -2036,16 +2079,18 @@ void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, Q
}
#if QT_CONFIG(quick_path)
-void QQuickJSContext2D::method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_path(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = r->d()->context->m_v4path.value();
+ RETURN_RESULT(r->d()->context->m_v4path.value());
}
-void QQuickJSContext2D::method_set_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_path(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
@@ -2069,19 +2114,20 @@ void QQuickJSContext2D::method_set_path(const QV4::BuiltinFunction *, QV4::Scope
\qmlmethod object QtQuick::Context2D::clearRect(real x, real y, real w, real h)
Clears all pixels on the canvas in the given rectangle to transparent black.
*/
-void QQuickJSContext2DPrototype::method_clearRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_clearRect(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 4)
+ if (callData->argc() >= 4)
r->d()->context->clearRect(callData->args[0].toNumber(),
callData->args[1].toNumber(),
callData->args[2].toNumber(),
callData->args[3].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2090,14 +2136,15 @@ void QQuickJSContext2DPrototype::method_clearRect(const QV4::BuiltinFunction *,
\sa fillStyle
*/
-void QQuickJSContext2DPrototype::method_fillRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillRect(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 4)
+ if (callData->argc() >= 4)
r->d()->context->fillRect(callData->args[0].toNumber(), callData->args[1].toNumber(), callData->args[2].toNumber(), callData->args[3].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2111,15 +2158,16 @@ void QQuickJSContext2DPrototype::method_fillRect(const QV4::BuiltinFunction *, Q
\sa lineJoin
\sa miterLimit
*/
-void QQuickJSContext2DPrototype::method_strokeRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 4)
+ if (callData->argc() >= 4)
r->d()->context->strokeRect(callData->args[0].toNumber(), callData->args[1].toNumber(), callData->args[2].toNumber(), callData->args[3].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2144,15 +2192,16 @@ void QQuickJSContext2DPrototype::method_strokeRect(const QV4::BuiltinFunction *,
\sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
Context Standard for arc()}
*/
-void QQuickJSContext2DPrototype::method_arc(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_arc(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 5) {
+ if (callData->argc() >= 5) {
bool antiClockwise = false;
- if (callData->argc == 6)
+ if (callData->argc() == 6)
antiClockwise = callData->args[5].toBoolean();
qreal radius = callData->args[2].toNumber();
@@ -2168,7 +2217,7 @@ void QQuickJSContext2DPrototype::method_arc(const QV4::BuiltinFunction *, QV4::S
antiClockwise);
}
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2195,12 +2244,13 @@ void QQuickJSContext2DPrototype::method_arc(const QV4::BuiltinFunction *, QV4::S
\sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C's 2D
Context Standard for arcTo()}
*/
-void QQuickJSContext2DPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_arcTo(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 5) {
+ if (callData->argc() >= 5) {
qreal radius = callData->args[4].toNumber();
if (qt_is_finite(radius) && radius < 0)
@@ -2213,7 +2263,7 @@ void QQuickJSContext2DPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4:
radius);
}
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2222,14 +2272,15 @@ void QQuickJSContext2DPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4:
Resets the current path to a new path.
*/
-void QQuickJSContext2DPrototype::method_beginPath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_beginPath(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
r->d()->context->beginPath();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2252,14 +2303,13 @@ void QQuickJSContext2DPrototype::method_beginPath(const QV4::BuiltinFunction *,
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
\sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
*/
-void QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 6) {
+ if (callData->argc() >= 6) {
qreal cp1x = callData->args[0].toNumber();
qreal cp1y = callData->args[1].toNumber();
qreal cp2x = callData->args[2].toNumber();
@@ -2268,10 +2318,11 @@ void QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::BuiltinFunction
qreal y = callData->args[5].toNumber();
if (!qt_is_finite(cp1x) || !qt_is_finite(cp1y) || !qt_is_finite(cp2x) || !qt_is_finite(cp2y) || !qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
r->d()->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
}
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2298,13 +2349,14 @@ void QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::BuiltinFunction
\sa fill()
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
*/
-void QQuickJSContext2DPrototype::method_clip(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_clip(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
r->d()->context->clip();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2314,15 +2366,15 @@ void QQuickJSContext2DPrototype::method_clip(const QV4::BuiltinFunction *, QV4::
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
*/
-void QQuickJSContext2DPrototype::method_closePath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_closePath(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
-
r->d()->context->closePath();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2334,12 +2386,13 @@ void QQuickJSContext2DPrototype::method_closePath(const QV4::BuiltinFunction *,
\sa fillStyle
*/
-void QQuickJSContext2DPrototype::method_fill(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_fill(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r);
r->d()->context->fill();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2347,22 +2400,23 @@ void QQuickJSContext2DPrototype::method_fill(const QV4::BuiltinFunction *, QV4::
Draws a line from the current position to the point (x, y).
*/
-void QQuickJSContext2DPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_lineTo(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 2) {
+ if (callData->argc() >= 2) {
qreal x = callData->args[0].toNumber();
qreal y = callData->args[1].toNumber();
if (!qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
r->d()->context->lineTo(x, y);
}
+
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2370,21 +2424,22 @@ void QQuickJSContext2DPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4
Creates a new subpath with the given point.
*/
-void QQuickJSContext2DPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_moveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 2) {
+ if (callData->argc() >= 2) {
qreal x = callData->args[0].toNumber();
qreal y = callData->args[1].toNumber();
if (!qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
r->d()->context->moveTo(x, y);
}
+
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2394,24 +2449,25 @@ void QQuickJSContext2DPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4
See \l{http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for quadraticCurveTo}
*/
-void QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 4) {
+ if (callData->argc() >= 4) {
qreal cpx = callData->args[0].toNumber();
qreal cpy = callData->args[1].toNumber();
qreal x = callData->args[2].toNumber();
qreal y = callData->args[3].toNumber();
if (!qt_is_finite(cpx) || !qt_is_finite(cpy) || !qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
r->d()->context->quadraticCurveTo(cpx, cpy, x, y);
}
+
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2419,14 +2475,15 @@ void QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::BuiltinFunct
Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
*/
-void QQuickJSContext2DPrototype::method_rect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_rect(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 4)
+ if (callData->argc() >= 4)
r->d()->context->rect(callData->args[0].toNumber(), callData->args[1].toNumber(), callData->args[2].toNumber(), callData->args[3].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2436,19 +2493,20 @@ void QQuickJSContext2DPrototype::method_rect(const QV4::BuiltinFunction *, QV4::
Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
ellipses defining the corners of the rounded rectangle.
*/
-void QQuickJSContext2DPrototype::method_roundedRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_roundedRect(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 6)
+ if (callData->argc() >= 6)
r->d()->context->roundedRect(callData->args[0].toNumber()
, callData->args[1].toNumber()
, callData->args[2].toNumber()
, callData->args[3].toNumber()
, callData->args[4].toNumber()
, callData->args[5].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2460,16 +2518,16 @@ void QQuickJSContext2DPrototype::method_roundedRect(const QV4::BuiltinFunction *
The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
*/
-void QQuickJSContext2DPrototype::method_ellipse(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_ellipse(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
-
- if (callData->argc >= 4)
+ if (callData->argc() >= 4)
r->d()->context->ellipse(callData->args[0].toNumber(), callData->args[1].toNumber(), callData->args[2].toNumber(), callData->args[3].toNumber());
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2479,22 +2537,22 @@ void QQuickJSContext2DPrototype::method_ellipse(const QV4::BuiltinFunction *, QV
Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
*/
-void QQuickJSContext2DPrototype::method_text(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_text(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 3) {
+ if (callData->argc() >= 3) {
qreal x = callData->args[1].toNumber();
qreal y = callData->args[2].toNumber();
if (!qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
r->d()->context->text(callData->args[0].toQStringNoThrow(), x, y);
}
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2506,13 +2564,14 @@ void QQuickJSContext2DPrototype::method_text(const QV4::BuiltinFunction *, QV4::
\sa strokeStyle
*/
-void QQuickJSContext2DPrototype::method_stroke(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_stroke(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
r->d()->context->stroke();
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
@@ -2523,29 +2582,33 @@ void QQuickJSContext2DPrototype::method_stroke(const QV4::BuiltinFunction *, QV4
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
*/
-void QQuickJSContext2DPrototype::method_isPointInPath(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
bool pointInPath = false;
- if (callData->argc >= 2)
+ if (callData->argc() >= 2)
pointInPath = r->d()->context->isPointInPath(callData->args[0].toNumber(), callData->args[1].toNumber());
- scope.result = QV4::Primitive::fromBoolean(pointInPath).asReturnedValue();
+ RETURN_RESULT(QV4::Primitive::fromBoolean(pointInPath).asReturnedValue());
}
-void QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::BuiltinFunction *b, QV4::CallData *)
{
+ QV4::Scope scope(b);
THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
}
-void QQuickJSContext2DPrototype::method_setCaretSelectionRect(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_setCaretSelectionRect(const QV4::BuiltinFunction *b, QV4::CallData *)
{
+ QV4::Scope scope(b);
THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
}
-void QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::BuiltinFunction *b, QV4::CallData *)
{
+ QV4::Scope scope(b);
THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
}
@@ -2572,16 +2635,18 @@ void QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::BuiltinFunctio
The default font value is "10px sans-serif".
*/
-void QQuickJSContext2D::method_get_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_font(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
RETURN_RESULT(scope.engine->newString(r->d()->context->state.font.toString()));
}
-void QQuickJSContext2D::method_set_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
@@ -2609,8 +2674,9 @@ void QQuickJSContext2D::method_set_font(const QV4::BuiltinFunction *, QV4::Scope
\endlist
Other values are ignored. The default value is "start".
*/
-void QQuickJSContext2D::method_get_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
@@ -2630,8 +2696,9 @@ void QQuickJSContext2D::method_get_textAlign(const QV4::BuiltinFunction *, QV4::
RETURN_RESULT(scope.engine->newString(QStringLiteral("start")));
}
-void QQuickJSContext2D::method_set_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
@@ -2675,8 +2742,9 @@ void QQuickJSContext2D::method_set_textAlign(const QV4::BuiltinFunction *, QV4::
\endlist
Other values are ignored. The default value is "alphabetic".
*/
-void QQuickJSContext2D::method_get_textBaseline(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
@@ -2696,8 +2764,9 @@ void QQuickJSContext2D::method_get_textBaseline(const QV4::BuiltinFunction *, QV
RETURN_RESULT(scope.engine->newString(QStringLiteral("alphabetic")));
}
-void QQuickJSContext2D::method_set_textBaseline(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT_SETTER(r)
QV4::ScopedString s(scope, callData->argument(0), QV4::ScopedString::Convert);
@@ -2733,21 +2802,22 @@ void QQuickJSContext2D::method_set_textBaseline(const QV4::BuiltinFunction *, QV
\sa textBaseline
\sa strokeText
*/
-void QQuickJSContext2DPrototype::method_fillText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillText(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
- if (callData->argc >= 3) {
+ if (callData->argc() >= 3) {
qreal x = callData->args[1].toNumber();
qreal y = callData->args[2].toNumber();
if (!qt_is_finite(x) || !qt_is_finite(y))
- return;
+ RETURN_UNDEFINED();
QPainterPath textPath = r->d()->context->createTextGlyphs(x, y, callData->args[0].toQStringNoThrow());
r->d()->context->buffer()->fill(textPath);
}
+
+ RETURN_RESULT(callData->thisObject);
}
/*!
\qmlmethod object QtQuick::Context2D::strokeText(text, x, y)
@@ -2757,15 +2827,16 @@ void QQuickJSContext2DPrototype::method_fillText(const QV4::BuiltinFunction *, Q
\sa textBaseline
\sa fillText
*/
-void QQuickJSContext2DPrototype::method_strokeText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeText(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 3)
+ if (callData->argc() >= 3)
r->d()->context->drawText(callData->args[0].toQStringNoThrow(), callData->args[1].toNumber(), callData->args[2].toNumber(), false);
- scope.result = callData->thisObject;
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -2774,18 +2845,19 @@ void QQuickJSContext2DPrototype::method_strokeText(const QV4::BuiltinFunction *,
Returns an object with a \c width property, whose value is equivalent to
calling \l {QFontMetrics::width()} with the given \a text in the current font.
*/
-void QQuickJSContext2DPrototype::method_measureText(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- if (callData->argc >= 1) {
+ if (callData->argc() >= 1) {
QFontMetrics fm(r->d()->context->state.font);
uint width = fm.width(callData->args[0].toQStringNoThrow());
QV4::ScopedObject tm(scope, scope.engine->newObject());
tm->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
QV4::ScopedValue(scope, QV4::Primitive::fromDouble(width)));
- RETURN_RESULT(tm);
+ RETURN_RESULT(*tm);
}
RETURN_UNDEFINED();
}
@@ -2849,21 +2921,20 @@ void QQuickJSContext2DPrototype::method_measureText(const QV4::BuiltinFunction *
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
*/
-void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject);
CHECK_CONTEXT(r)
- scope.result = callData->thisObject;
-
qreal sx, sy, sw, sh, dx, dy, dw, dh;
- if (!callData->argc)
- return;
+ if (!callData->argc())
+ RETURN_UNDEFINED();
//FIXME:This function should be moved to QQuickContext2D::drawImage(...)
if (!r->d()->context->state.invertibleCTM)
- return;
+ RETURN_UNDEFINED();
QQmlRefPointer<QQuickCanvasPixmap> pixmap;
@@ -2908,9 +2979,9 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
}
if (pixmap.isNull() || !pixmap->isValid())
- return;
+ RETURN_UNDEFINED();
- if (callData->argc >= 9) {
+ if (callData->argc() >= 9) {
sx = callData->args[1].toNumber();
sy = callData->args[2].toNumber();
sw = callData->args[3].toNumber();
@@ -2919,7 +2990,7 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
dy = callData->args[6].toNumber();
dw = callData->args[7].toNumber();
dh = callData->args[8].toNumber();
- } else if (callData->argc >= 5) {
+ } else if (callData->argc() >= 5) {
sx = 0;
sy = 0;
sw = pixmap->width();
@@ -2928,7 +2999,7 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
dy = callData->args[2].toNumber();
dw = callData->args[3].toNumber();
dh = callData->args[4].toNumber();
- } else if (callData->argc >= 3) {
+ } else if (callData->argc() >= 3) {
dx = callData->args[1].toNumber();
dy = callData->args[2].toNumber();
sx = 0;
@@ -2938,7 +3009,7 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
dw = sw;
dh = sh;
} else {
- return;
+ RETURN_UNDEFINED();
}
if (!qt_is_finite(sx)
@@ -2949,7 +3020,7 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
|| !qt_is_finite(dy)
|| !qt_is_finite(dw)
|| !qt_is_finite(dh))
- return;
+ RETURN_UNDEFINED();
if (sx < 0
|| sy < 0
@@ -2962,6 +3033,8 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
}
r->d()->context->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
+
+ RETURN_RESULT(callData->thisObject);
}
// pixel manipulation
@@ -2988,40 +3061,43 @@ void QQuickJSContext2DPrototype::method_drawImage(const QV4::BuiltinFunction *,
\qmlproperty int QtQuick::CanvasImageData::width
Holds the actual width dimension of the data in the ImageData object, in device pixels.
*/
-void QQuickJSContext2DImageData::method_get_width(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, callData->thisObject);
if (!imageData)
THROW_TYPE_ERROR();
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
int width = r ? r->d()->image->width() : 0;
- scope.result = QV4::Encode(width);
+ RETURN_RESULT(QV4::Encode(width));
}
/*!
\qmlproperty int QtQuick::CanvasImageData::height
Holds the actual height dimension of the data in the ImageData object, in device pixels.
*/
-void QQuickJSContext2DImageData::method_get_height(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, callData->thisObject);
if (!imageData)
THROW_TYPE_ERROR();
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
int height = r ? r->d()->image->height() : 0;
- scope.result = QV4::Encode(height);
+ RETURN_RESULT(QV4::Encode(height));
}
/*!
\qmlproperty object QtQuick::CanvasImageData::data
Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
*/
-void QQuickJSContext2DImageData::method_get_data(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DImageData::method_get_data(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, callData->thisObject);
if (!imageData)
THROW_TYPE_ERROR();
- scope.result = imageData->d()->pixelData;
+ RETURN_RESULT(imageData->d()->pixelData);
}
/*!
@@ -3042,8 +3118,9 @@ void QQuickJSContext2DImageData::method_get_data(const QV4::BuiltinFunction *, Q
The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
This property is read only.
*/
-void QQuickJSContext2DPixelData::proto_get_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, callData->thisObject.as<QQuickJSContext2DPixelData>());
if (!r || r->d()->image->isNull())
RETURN_UNDEFINED();
@@ -3140,12 +3217,13 @@ bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q
\sa Canvas::loadImage(), QtQuick::Canvas::unloadImage(),
QtQuick::Canvas::isImageLoaded
*/
-void QQuickJSContext2DPrototype::method_createImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc == 1) {
+ if (callData->argc() == 1) {
QV4::ScopedValue arg0(scope, callData->args[0]);
QV4::Scoped<QQuickJSContext2DImageData> imgData(scope, arg0);
if (!!imgData) {
@@ -3159,7 +3237,7 @@ void QQuickJSContext2DPrototype::method_createImageData(const QV4::BuiltinFuncti
QImage image = r->d()->context->createPixmap(QUrl(arg0->toQStringNoThrow()))->image();
RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, image));
}
- } else if (callData->argc == 2) {
+ } else if (callData->argc() == 2) {
qreal w = callData->args[0].toNumber();
qreal h = callData->args[1].toNumber();
@@ -3178,12 +3256,13 @@ void QQuickJSContext2DPrototype::method_createImageData(const QV4::BuiltinFuncti
\qmlmethod CanvasImageData QtQuick::Context2D::getImageData(real sx, real sy, real sw, real sh)
Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
*/
-void QQuickJSContext2DPrototype::method_getImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc >= 4) {
+ if (callData->argc() >= 4) {
qreal x = callData->args[0].toNumber();
qreal y = callData->args[1].toNumber();
qreal w = callData->args[2].toNumber();
@@ -3197,18 +3276,19 @@ void QQuickJSContext2DPrototype::method_getImageData(const QV4::BuiltinFunction
QImage image = r->d()->context->canvas()->toImage(QRectF(x, y, w, h));
RETURN_RESULT(qt_create_image_data(w, h, scope.engine, image));
}
- scope.result = QV4::Encode::null();
+ RETURN_RESULT(QV4::Encode::null());
}
/*!
\qmlmethod object QtQuick::Context2D::putImageData(CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted.
*/
-void QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject.as<QQuickJSContext2D>());
CHECK_CONTEXT(r)
- if (callData->argc < 7)
+ if (callData->argc() < 7)
RETURN_UNDEFINED();
QV4::ScopedValue arg0(scope, callData->args[0]);
@@ -3222,18 +3302,16 @@ void QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction
if (!qt_is_finite(dx) || !qt_is_finite(dy))
THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
- scope.result = callData->thisObject;
-
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg0);
if (!imageData)
- return;
+ RETURN_UNDEFINED();
QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (pixelArray) {
w = pixelArray->d()->image->width();
h = pixelArray->d()->image->height();
- if (callData->argc == 7) {
+ if (callData->argc() == 7) {
dirtyX = callData->args[3].toNumber();
dirtyY = callData->args[4].toNumber();
dirtyWidth = callData->args[5].toNumber();
@@ -3272,7 +3350,7 @@ void QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction
}
if (dirtyWidth <=0 || dirtyHeight <= 0)
- return;
+ RETURN_UNDEFINED();
} else {
dirtyX = 0;
dirtyY = 0;
@@ -3283,6 +3361,8 @@ void QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction
QImage image = pixelArray->d()->image->copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
r->d()->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
}
+
+ RETURN_RESULT(callData->thisObject);
}
/*!
@@ -3305,13 +3385,14 @@ void QQuickJSContext2DPrototype::method_putImageData(const QV4::BuiltinFunction
gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
\endcode
*/
-void QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
+QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::BuiltinFunction *b, QV4::CallData *callData)
{
+ QV4::Scope scope(b);
QV4::Scoped<QQuickContext2DStyle> style(scope, callData->thisObject.as<QQuickContext2DStyle>());
if (!style)
THROW_GENERIC_ERROR("Not a CanvasGradient object");
- if (callData->argc == 2) {
+ if (callData->argc() == 2) {
if (!style->d()->brush->gradient())
THROW_GENERIC_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
@@ -3336,7 +3417,7 @@ void QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::BuiltinFunctio
*style->d()->brush = gradient;
}
- scope.result = callData->thisObject;
+ return callData->thisObject.asReturnedValue();
}
void QQuickContext2D::scale(qreal x, qreal y)
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 6579175cc6..ec4adf1ccd 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -8541,25 +8541,25 @@ QAccessible::Role QQuickItemPrivate::accessibleRole() const
namespace QV4 {
namespace Heap {
struct QQuickItemWrapper : public QObjectWrapper {
+ static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
};
}
}
struct QQuickItemWrapper : public QV4::QObjectWrapper {
V4_OBJECT2(QQuickItemWrapper, QV4::QObjectWrapper)
- static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
};
DEFINE_OBJECT_VTABLE(QQuickItemWrapper);
-void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
+void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
{
- QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
+ QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
for (QQuickItem *child : qAsConst(QQuickItemPrivate::get(item)->childItems))
QV4::QObjectWrapper::markWrapper(child, markStack);
}
- QV4::QObjectWrapper::markObjects(that, markStack);
+ QObjectWrapper::markObjects(that, markStack);
}
quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
index 33b294593f..3cd359cf48 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -1189,6 +1189,14 @@ void tst_QQmlDebugJS::stepNext()
QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE));
}
+static QVariantMap responseBody(QJSDebugClient *client)
+{
+ const QString jsonString(client->response);
+ const QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString))
+ .toVariant().toMap();
+ return value.value("body").toMap();
+}
+
void tst_QQmlDebugJS::stepIn()
{
//void continueDebugging(StepAction stepAction, int stepCount = 1);
@@ -1197,21 +1205,18 @@ void tst_QQmlDebugJS::stepIn()
QFETCH(bool, namesAsObjects);
int sourceLine = 41;
- int actualLine = 37;
+ int actualLine = 36;
QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true);
m_client->connect(redundantRefs, namesAsObjects);
QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(stopped())));
+ QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine);
m_client->continueDebugging(QJSDebugClient::In);
QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(stopped())));
- QString jsonString(m_client->response);
- QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap();
-
- QVariantMap body = value.value("body").toMap();
-
+ const QVariantMap body = responseBody(m_client);
QCOMPARE(body.value("sourceLine").toInt(), actualLine);
QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE));
}
@@ -1230,15 +1235,12 @@ void tst_QQmlDebugJS::stepOut()
m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true);
m_client->connect(redundantRefs, namesAsObjects);
QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(stopped())));
+ QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine);
m_client->continueDebugging(QJSDebugClient::Out);
QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(stopped())));
- QString jsonString(m_client->response);
- QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap();
-
- QVariantMap body = value.value("body").toMap();
-
+ const QVariantMap body = responseBody(m_client);
QCOMPARE(body.value("sourceLine").toInt(), actualLine);
QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE));
}
@@ -1332,16 +1334,15 @@ void tst_QQmlDebugJS::evaluateInGlobalScope()
m_client->connect();
- do {
+ for (int i = 0; i < 10; ++i) {
// The engine might not be initialized, yet. We just try until it shows up.
m_client->evaluate(QLatin1String("console.log('Hello World')"));
- } while (!QQmlDebugTest::waitForSignal(m_client, SIGNAL(result()), 500));
+ if (QQmlDebugTest::waitForSignal(m_client, SIGNAL(result()), 500))
+ break;
+ }
//Verify the return value of 'console.log()', which is "undefined"
- QString jsonString(m_client->response);
- QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap();
- QVariantMap body = value.value("body").toMap();
- QCOMPARE(body.value("type").toString(),QLatin1String("undefined"));
+ QCOMPARE(responseBody(m_client).value("type").toString(), QLatin1String("undefined"));
}
void tst_QQmlDebugJS::evaluateInLocalScope()
@@ -1426,11 +1427,7 @@ void tst_QQmlDebugJS::evaluateInContext()
m_client->evaluate(QLatin1String("a + 10"), -1, object.debugId);
QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(result())));
- QString jsonString = m_client->response;
- QVariantMap value = m_client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap();
-
- QVariantMap body = value.value("body").toMap();
- QTRY_COMPARE(body.value("value").toInt(), 20);
+ QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 20);
}
void tst_QQmlDebugJS::getScripts()
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index fd74135727..9da4a63ce6 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -38,7 +38,6 @@
#include <private/qv4debugging_p.h>
#include <private/qv8engine_p.h>
#include <private/qv4objectiterator_p.h>
-#include <private/qv4isel_moth_p.h>
#include <private/qv4string_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugservice_p.h>
@@ -46,7 +45,7 @@
using namespace QV4;
using namespace QV4::Debugging;
-typedef void (*InjectedFunction)(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
+typedef QV4::ReturnedValue (*InjectedFunction)(const QV4::BuiltinFunction *, QV4::CallData *callData);
Q_DECLARE_METATYPE(InjectedFunction)
static bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000)
@@ -343,7 +342,6 @@ void tst_qv4debugger::init()
m_javaScriptThread = new QThread;
m_engine = new TestEngine;
m_v4 = m_engine->v4Engine();
- m_v4->iselFactory.reset(new QV4::Moth::ISelFactory);
m_v4->setDebugger(new QV4Debugger(m_v4));
m_engine->moveToThread(m_javaScriptThread);
m_javaScriptThread->start();
@@ -438,9 +436,9 @@ void tst_qv4debugger::addBreakPointWhilePaused()
QCOMPARE(state.lineNumber, 2);
}
-static void someCall(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *)
+static QV4::ReturnedValue someCall(const QV4::BuiltinFunction *function, QV4::CallData *)
{
- static_cast<QV4Debugger *>(scope.engine->debugger())
+ static_cast<QV4Debugger *>(function->engine()->debugger())
->removeBreakPoint("removeBreakPointForNextInstruction", 2);
RETURN_UNDEFINED();
}
@@ -464,7 +462,7 @@ void tst_qv4debugger::conditionalBreakPoint()
{
m_debuggerAgent->m_captureContextInfo = true;
QString script =
- "function test() {\n"
+ "var test = function() {\n"
" for (var i = 0; i < 15; ++i) {\n"
" var x = i;\n"
" }\n"
@@ -491,7 +489,6 @@ void tst_qv4debugger::conditionalBreakPointInQml()
QQmlEngine engine;
QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine);
QV4Debugger *v4Debugger = new QV4Debugger(v4);
- v4->iselFactory.reset(new QV4::Moth::ISelFactory);
v4->setDebugger(v4Debugger);
QScopedPointer<QThread> debugThread(new QThread);
@@ -531,7 +528,7 @@ void tst_qv4debugger::readArguments()
m_debuggerAgent->m_captureContextInfo = true;
QString script =
- "function f(a, b, c, d) {\n"
+ "var f = function(a, b, c, d) {\n"
" return a === b\n"
"}\n"
"var four;\n"
@@ -557,7 +554,7 @@ void tst_qv4debugger::readLocals()
m_debuggerAgent->m_captureContextInfo = true;
QString script =
- "function f(a, b) {\n"
+ "var f = function(a, b) {\n"
" var c = a + b\n"
" var d = a - b\n" // breakpoint, c should be set, d should be undefined
" return c === d\n"
@@ -583,7 +580,7 @@ void tst_qv4debugger::readObject()
m_debuggerAgent->m_captureContextInfo = true;
QString script =
- "function f(a) {\n"
+ "var f = function(a) {\n"
" var b = a\n"
" return b\n"
"}\n"
@@ -641,7 +638,7 @@ void tst_qv4debugger::readContextInAllFrames()
m_debuggerAgent->m_captureContextInfo = true;
QString script =
- "function fact(n) {\n"
+ "var fact = function(n) {\n"
" if (n > 1) {\n"
" var n_1 = n - 1;\n"
" n_1 = fact(n_1);\n"
@@ -814,13 +811,13 @@ void tst_qv4debugger::lastLineOfConditional_data()
QTest::newRow("do..while {block}") << "do {\n" << "} while (ret < 10);" << 4 << 7;
QTest::newRow("if true {block}") << "if (true) {\n" << "}"
- << 4 << 7;
+ << 4 << 8;
QTest::newRow("if false {block}") << "if (false) {\n" << "}"
<< 2 << 8;
QTest::newRow("if true else {block}") << "if (true) {\n" << "} else {\n ret += 8;\n}"
- << 4 << 7;
+ << 4 << 10;
QTest::newRow("if false else {block}") << "if (false) {\n" << "} else {\n ret += 8;\n}"
- << 8 << 9;
+ << 8 << 10;
QTest::newRow("for statement") << "for (var i = 0; i < 10; ++i)\n" << "" << 4 << 2;
QTest::newRow("for..in statement") << "for (var i in [0, 1, 2, 3, 4])\n" << "" << 4 << 2;
@@ -829,11 +826,11 @@ void tst_qv4debugger::lastLineOfConditional_data()
// For two nested if statements without blocks, we need to map the jump from the inner to the
// outer one on the outer "if". There is just no better place.
- QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 2;
+ QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 8;
QTest::newRow("if false statement") << "if (false)\n" << "" << 2 << 8;
// Also two nested ifs without blocks.
- QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 2;
+ QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 9;
QTest::newRow("if false else statement") << "if (false)\n" << "else\n ret += 8;" << 8 << 9;
}
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 27498de473..589f25d174 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -15,7 +15,6 @@ S15.1.2.3_A2_T10
15.5.4.20-3-5
15.5.4.20-3-6
-10.4.3-1-106 failing
11.2.3-3_3 failing
S13_A15_T4 failing
S15.4.4.3_A1_T1 failing
diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262
-Subproject fe58b18c13d1ea5d42951e5b698efc3bfab6dc8
+Subproject d60c4ed97e69639bc5bc1db43a98828debf80c8
diff --git a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
index 880e254543..4303aec2a7 100644
--- a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
+++ b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp
@@ -35,28 +35,17 @@ class tst_EcmaScriptTests : public QObject
{
Q_OBJECT
private slots:
- void runTests_data();
void runTests();
};
-void tst_EcmaScriptTests::runTests_data()
-{
- QTest::addColumn<QString>("qmljsParameter");
-
- QTest::newRow("jit") << QStringLiteral("--jit");
- QTest::newRow("interpreter") << QStringLiteral("--interpret");
-}
-
void tst_EcmaScriptTests::runTests()
{
#if defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64)
- QFETCH(QString, qmljsParameter);
-
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.setWorkingDirectory(QLatin1String(SRCDIR));
process.setProgram("python");
- process.setArguments(QStringList() << "test262.py" << "--command=" + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs " + qmljsParameter << "--parallel" << "--with-test-expectations");
+ process.setArguments(QStringList() << "test262.py" << "--command=" + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs" << "--parallel" << "--with-test-expectations");
qDebug() << "Going to run" << process.program() << process.arguments() << "in" << process.workingDirectory();
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 05528eea30..8471d6466f 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -35,7 +35,6 @@ PUBLICTESTS += \
PRIVATETESTS += \
qqmlcpputils \
qqmldirparser \
- v4misc \
qmlcachegen
PRIVATETESTS += \
@@ -77,7 +76,7 @@ qtHaveModule(widgets) {
PUBLICTESTS += \
qjsengine \
qjsvalue \
- qwidgetsinqml
+# qwidgetsinqml
}
SUBDIRS += $$PUBLICTESTS
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
index e75e51ed29..cdc9f29aab 100644
--- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -30,10 +30,9 @@
#include <private/qv4compileddata_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qv4jsir_p.h>
-#include <private/qv4isel_p.h>
#include <private/qv8engine_p.h>
#include <private/qv4engine_p.h>
+#include <private/qv4codegen_p.h>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlFileSelector>
@@ -174,9 +173,8 @@ struct TestCompiler
bool verify()
{
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
- return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), v4->iselFactory.data(), &lastErrorString);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
+ return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), &lastErrorString);
}
void closeMapping()
@@ -231,12 +229,14 @@ void tst_qmldiskcache::regenerateAfterChange()
const QV4::CompiledData::Object *obj = testUnit->objectAt(0);
QCOMPARE(quint32(obj->nBindings), quint32(1));
QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script));
- QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(1));
+ QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(0));
- QCOMPARE(quint32(testUnit->functionTableSize), quint32(2));
+ QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
- const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1);
- QVERIFY(bindingFunction->codeOffset > testUnit->unitSize);
+ const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(0);
+ QCOMPARE(testUnit->stringAt(bindingFunction->nameIndex), QString("expression for blah")); // check if we have the correct function
+ QVERIFY(bindingFunction->codeSize > 0);
+ QVERIFY(bindingFunction->codeOffset < testUnit->unitSize);
}
{
@@ -257,10 +257,12 @@ void tst_qmldiskcache::regenerateAfterChange()
QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number));
QCOMPARE(obj->bindingTable()->valueAsNumber(), double(42));
- QCOMPARE(quint32(testUnit->functionTableSize), quint32(2));
+ QCOMPARE(quint32(testUnit->functionTableSize), quint32(1));
- const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1);
- QVERIFY(bindingFunction->codeOffset > testUnit->unitSize);
+ const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(0);
+ QCOMPARE(testUnit->stringAt(bindingFunction->nameIndex), QString("expression for blah")); // check if we have the correct function
+ QVERIFY(bindingFunction->codeSize > 0);
+ QVERIFY(bindingFunction->codeOffset < testUnit->unitSize);
}
}
@@ -364,7 +366,7 @@ void tst_qmldiskcache::basicVersionChecks()
});
QVERIFY(!testCompiler.verify());
- QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName));
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QStringLiteral("moth")));
}
}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 4388e1412c..4415e6cf74 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -43,6 +43,7 @@
#include "../../shared/util.h"
#include <private/qv4functionobject_p.h>
#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
#include <private/qv4alloca_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4object_p.h>
@@ -342,6 +343,8 @@ private slots:
void freeze_empty_object();
void singleBlockLoops();
void qtbug_60547();
+ void delayLoadingArgs();
+ void manyArguments();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -2346,7 +2349,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const
QLatin1String(source) + QLatin1String(" })");
QV4::Scope scope(QV8Engine::getV4(engine));
- QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource);
+ QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource);
program.inheritContext = true;
QV4::ScopedFunctionObject function(scope, program.run());
@@ -2354,10 +2357,10 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const
scope.engine->catchException();
return true;
}
- QV4::ScopedCallData d(scope, 1);
- d->args[0] = o;
- d->thisObject = engine->global();
- function->call(scope, d);
+ QV4::JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = o;
+ *jsCallData->thisObject = engine->global();
+ function->call(jsCallData);
if (scope.engine->hasException) {
scope.engine->catchException();
return true;
@@ -2372,7 +2375,7 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o,
QLatin1String(source) + QLatin1String(" })");
QV4::Scope scope(QV8Engine::getV4(engine));
- QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource);
+ QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource);
program.inheritContext = true;
QV4::ScopedFunctionObject function(scope, program.run());
@@ -2383,15 +2386,16 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o,
if (!function)
return false;
- QV4::ScopedCallData d(scope, 1);
- d->args[0] = o;
- d->thisObject = engine->global();
- function->call(scope, d);
+ QV4::ScopedValue value(scope);
+ QV4::JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = o;
+ *jsCallData->thisObject = engine->global();
+ value = function->call(jsCallData);
if (scope.engine->hasException) {
scope.engine->catchException();
return false;
}
- return QV4::Runtime::method_strictEqual(scope.result, result);
+ return QV4::Runtime::method_strictEqual(value, result);
}
static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o,
@@ -2402,7 +2406,7 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o
QV4::Scope scope(QV8Engine::getV4(engine));
- QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), functionSource);
+ QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource);
program.inheritContext = true;
QV4::ScopedFunctionObject function(scope, program.run());
@@ -2412,15 +2416,15 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o
}
if (!function)
return QV4::Encode::undefined();
- QV4::ScopedCallData d(scope, 1);
- d->args[0] = o;
- d->thisObject = engine->global();
- function->call(scope, d);
+ QV4::JSCallData jsCallData(scope, 1);
+ jsCallData->args[0] = o;
+ *jsCallData->thisObject = engine->global();
+ QV4::ScopedValue result(scope, function->call(jsCallData));
if (scope.engine->hasException) {
scope.engine->catchException();
return QV4::Encode::undefined();
}
- return scope.result.asReturnedValue();
+ return result->asReturnedValue();
}
#define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
@@ -8373,6 +8377,24 @@ void tst_qqmlecmascript::qtbug_60547()
QCOMPARE(object->property("counter"), QVariant(int(1)));
}
+void tst_qqmlecmascript::delayLoadingArgs()
+{
+ QJSEngine engine;
+ QJSValue ret = engine.evaluate("(function(x){return x + (x+=2)})(20)");
+ QCOMPARE(ret.toInt(), 42); // esp. not 44.
+}
+
+void tst_qqmlecmascript::manyArguments()
+{
+ const char *testCase =
+ "function x() { var sum; for (var i = 0; i < arguments.length; ++i) sum += arguments[i][0]; }"
+ "x([0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9])";
+
+ QJSEngine engine;
+ engine.evaluate(testCase);
+}
+
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST
index f35397f119..0e29c1049f 100644
--- a/tests/auto/quick/qquickflickable/BLACKLIST
+++ b/tests/auto/quick/qquickflickable/BLACKLIST
@@ -1,7 +1,7 @@
# QTBUG-36804
osx-10.10
[rebound]
-osx-10.10
+osx
[movingAndFlicking:vertical]
osx-10.10
[movingAndFlicking:both]
diff --git a/tests/manual/v4/fact.2.js b/tests/manual/v4/fact.2.js
index d8f750b5a1..1696e8b80d 100644
--- a/tests/manual/v4/fact.2.js
+++ b/tests/manual/v4/fact.2.js
@@ -3,6 +3,6 @@ function fact(n) {
return n > 1 ? n * fact(n - 1) : 1
}
-for (var i = 0; i < 1000000; i = i + 1)
+for (var i = 0; i < 100000; i = i + 1)
fact(12)
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index a62adc82f4..d8af157b8e 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -35,17 +35,7 @@
#include <QHashFunctions>
#include <private/qqmlirbuilder_p.h>
-#include <private/qv4isel_moth_p.h>
#include <private/qqmljsparser_p.h>
-#include <private/qv4jssimplifier_p.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 { namespace JIT {
-Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture);
-} }
-
-QT_END_NAMESPACE
struct Error
{
@@ -128,7 +118,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex));
if (!compiledFunction)
continue;
- if (compiledFunction->usesArgumentsObject) {
+ if (compiledFunction->usesArgumentsObject == QV4::Compiler::Context::ArgumentsObjectUsed) {
error->message = QLatin1Char(':') + QString::number(compiledFunction->line) + QLatin1Char(':');
if (compiledFunction->column > 0)
error->message += QString::number(compiledFunction->column) + QLatin1Char(':');
@@ -145,7 +135,7 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
return true;
}
-static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error)
+static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
irDocument.jsModule.targetABI = targetABI;
@@ -180,7 +170,11 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
annotateListElements(&irDocument);
{
- QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable);
+ QmlIR::JSCodeGen v4CodeGen(irDocument.code,
+ &irDocument.jsGenerator, &irDocument.jsModule,
+ &irDocument.jsParserEngine, irDocument.program,
+ /*import cache*/0, &irDocument.jsGenerator.stringTable);
+ v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
@@ -210,17 +204,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
}
QmlIR::QmlUnitGenerator generator;
-
- {
- QQmlJavaScriptBindingExpressionSimplificationPass pass(irDocument.objects, &irDocument.jsModule, &irDocument.jsGenerator);
- pass.reduceTranslationBindings();
- }
-
- QV4::ExecutableAllocator allocator;
- QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
- // Disable lookups in non-standalone (aka QML) mode
- isel->setUseFastLookups(false);
- irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false);
+ irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
QV4::CompiledData::Unit *unit = generator.generate(irDocument);
unit->flags |= QV4::CompiledData::Unit::StaticData;
unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation;
@@ -234,7 +218,7 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi
return true;
}
-static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error)
+static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, const QString &targetABI, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
irDocument.jsModule.targetABI = targetABI;
@@ -289,8 +273,13 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
}
{
- QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable);
- v4CodeGen.generateFromProgram(/*empty input file name*/QString(), sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode);
+ irDocument.jsModule.fileName = inputFileName;
+ QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
+ &irDocument.jsModule, &irDocument.jsParserEngine,
+ irDocument.program, /*import cache*/0,
+ &irDocument.jsGenerator.stringTable);
+ v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
+ v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QV4::Compiler::GlobalCode);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
if (!jsErrors.isEmpty()) {
for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) {
@@ -304,11 +293,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &outputFil
QmlIR::QmlUnitGenerator generator;
- QV4::ExecutableAllocator allocator;
- QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
- // Disable lookups in non-standalone (aka QML) mode
- isel->setUseFastLookups(false);
- irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false);
+ irDocument.javaScriptCompilationUnit = v4CodeGen.generateCompilationUnit(/*generate unit*/false);
QV4::CompiledData::Unit *unit = generator.generate(irDocument);
unit->flags |= QV4::CompiledData::Unit::StaticData;
irDocument.javaScriptCompilationUnit->data = unit;
@@ -360,17 +345,12 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- QScopedPointer<QV4::EvalISelFactory> isel;
- const QString targetArchitecture = parser.value(targetArchitectureOption);
-
- isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture));
-
- if (parser.isSet(checkIfSupportedOption)) {
- if (isel.isNull())
- return EXIT_FAILURE;
- else
- return EXIT_SUCCESS;
- }
+// if (parser.isSet(checkIfSupportedOption)) {
+// if (isel.isNull())
+// return EXIT_FAILURE;
+// else
+// return EXIT_SUCCESS;
+// }
const QStringList sources = parser.positionalArguments();
if (sources.isEmpty()){
@@ -381,9 +361,6 @@ int main(int argc, char **argv)
}
const QString inputFile = sources.first();
- if (!isel)
- isel.reset(new QV4::Moth::ISelFactory);
-
Error error;
QString outputFileName = inputFile + QLatin1Char('c');
@@ -393,12 +370,12 @@ int main(int argc, char **argv)
const QString targetABI = parser.value(targetABIOption);
if (inputFile.endsWith(QLatin1String(".qml"))) {
- if (!compileQmlFile(inputFile, outputFileName, isel.data(), targetABI, &error)) {
+ if (!compileQmlFile(inputFile, outputFileName, targetABI, &error)) {
error.augment(QLatin1String("Error compiling qml file: ")).print();
return EXIT_FAILURE;
}
} else if (inputFile.endsWith(QLatin1String(".js"))) {
- if (!compileJSFile(inputFile, outputFileName, isel.data(), targetABI, &error)) {
+ if (!compileJSFile(inputFile, outputFileName, targetABI, &error)) {
error.augment(QLatin1String("Error compiling qml file: ")).print();
return EXIT_FAILURE;
}
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index 182547490d..a8dd19228e 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -32,24 +32,13 @@
#include "private/qv4errorobject_p.h"
#include "private/qv4globalobject_p.h"
#include "private/qv4codegen_p.h"
-#if QT_CONFIG(qml_interpreter)
-#include "private/qv4isel_moth_p.h"
-#include "private/qv4vme_moth_p.h"
-#endif
#include "private/qv4objectproto_p.h"
-#include "private/qv4isel_p.h"
#include "private/qv4mm_p.h"
#include "private/qv4context_p.h"
#include "private/qv4script_p.h"
#include "private/qv4string_p.h"
#include "private/qqmlbuiltinfunctions_p.h"
-#ifdef V4_ENABLE_JIT
-# include "private/qv4isel_masm_p.h"
-#else
-QT_REQUIRE_CONFIG(qml_interpreter);
-#endif // V4_ENABLE_JIT
-
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
@@ -87,32 +76,10 @@ int main(int argc, char *argv[])
QStringList args = app.arguments();
args.removeFirst();
- enum {
- use_masm,
- use_moth
- } mode;
-#ifdef V4_ENABLE_JIT
- mode = use_masm;
-#else
- mode = use_moth;
-#endif
-
bool runAsQml = false;
bool cache = false;
if (!args.isEmpty()) {
- if (args.constFirst() == QLatin1String("--jit")) {
- mode = use_masm;
- args.removeFirst();
- }
-
-#if QT_CONFIG(qml_interpreter)
- if (args.constFirst() == QLatin1String("--interpret")) {
- mode = use_moth;
- args.removeFirst();
- }
-#endif
-
if (args.constFirst() == QLatin1String("--qml")) {
runAsQml = true;
args.removeFirst();
@@ -129,79 +96,66 @@ int main(int argc, char *argv[])
}
}
- switch (mode) {
- case use_masm:
- case use_moth: {
- QV4::EvalISelFactory* iSelFactory = 0;
- if (mode == use_moth) {
-#if QT_CONFIG(qml_interpreter)
- iSelFactory = new QV4::Moth::ISelFactory;
-#endif
-#ifdef V4_ENABLE_JIT
- } else {
- iSelFactory = new QV4::JIT::ISelFactory<>;
-#endif // V4_ENABLE_JIT
- }
-
- QV4::ExecutionEngine vm(iSelFactory);
+ QV4::ExecutionEngine vm;
- QV4::Scope scope(&vm);
- QV4::ScopedContext ctx(scope, vm.rootContext());
+ QV4::Scope scope(&vm);
+ QV4::ScopedContext ctx(scope, vm.rootContext());
- QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
+ QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
- for (const QString &fn : qAsConst(args)) {
- QFile file(fn);
- if (file.open(QFile::ReadOnly)) {
- QScopedPointer<QV4::Script> script;
- if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = iSelFactory->createUnitForLoading();
- QString error;
- if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), iSelFactory, &error)) {
- script.reset(new QV4::Script(&vm, nullptr, unit));
- } else {
- std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl;
- }
+ for (const QString &fn : qAsConst(args)) {
+ QFile file(fn);
+ if (file.open(QFile::ReadOnly)) {
+ QScopedPointer<QV4::Script> script;
+ if (cache && QFile::exists(fn + QLatin1Char('c'))) {
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Compiler::Codegen::createUnitForLoading();
+ QString error;
+ if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
+ script.reset(new QV4::Script(&vm, nullptr, unit));
+ } else {
+ std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl;
}
- if (!script) {
- const QString code = QString::fromUtf8(file.readAll());
- file.close();
+ }
+ if (!script) {
+ const QString code = QString::fromUtf8(file.readAll());
+ file.close();
- script.reset(new QV4::Script(ctx, code, fn));
- script->parseAsBinding = runAsQml;
- script->parse();
- }
- QV4::ScopedValue result(scope);
- if (!scope.engine->hasException) {
- const auto unit = script->compilationUnit;
- if (cache && unit && !(unit->data->flags & QV4::CompiledData::Unit::StaticData)) {
- if (unit->data->sourceTimeStamp == 0) {
- const_cast<QV4::CompiledData::Unit*>(unit->data)->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch();
- }
- QString saveError;
- if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) {
- std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl;
- }
+ script.reset(new QV4::Script(ctx, QV4::Compiler::GlobalCode, code, fn));
+ script->parseAsBinding = runAsQml;
+ script->parse();
+ }
+ QV4::ScopedValue result(scope);
+ if (!scope.engine->hasException) {
+ const auto unit = script->compilationUnit;
+ if (cache && unit && !(unit->data->flags & QV4::CompiledData::Unit::StaticData)) {
+ if (unit->data->sourceTimeStamp == 0) {
+ const_cast<QV4::CompiledData::Unit*>(unit->data)->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch();
+ }
+ QString saveError;
+ if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) {
+ std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl;
}
- result = script->run();
- }
- if (scope.engine->hasException) {
- QV4::StackTrace trace;
- QV4::ScopedValue ex(scope, scope.engine->catchException(&trace));
- showException(ctx, ex, trace);
- return EXIT_FAILURE;
- }
- if (!result->isUndefined()) {
- if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
- std::cout << "exit value: " << qPrintable(result->toQString()) << std::endl;
}
- } else {
- std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl;
+// QElapsedTimer t; t.start();
+ result = script->run();
+// std::cout << t.elapsed() << " ms. elapsed" << std::endl;
+ }
+ if (scope.engine->hasException) {
+ QV4::StackTrace trace;
+ QV4::ScopedValue ex(scope, scope.engine->catchException(&trace));
+ showException(ctx, ex, trace);
return EXIT_FAILURE;
}
+ if (!result->isUndefined()) {
+ if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
+ std::cout << "exit value: " << qPrintable(result->toQString()) << std::endl;
+ }
+ } else {
+ std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl;
+ return EXIT_FAILURE;
}
+ }
- vm.memoryManager->dumpStats();
- } return EXIT_SUCCESS;
- } // switch (mode)
+ vm.memoryManager->dumpStats();
+ return EXIT_SUCCESS;
}