aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
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 /src/qml
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
Diffstat (limited to 'src/qml')
-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
162 files changed, 15585 insertions, 29694 deletions
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