aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/v4
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/v4')
-rw-r--r--src/qml/qml/v4/llvm_installation.pri23
-rw-r--r--src/qml/qml/v4/llvm_runtime.cpp513
-rw-r--r--src/qml/qml/v4/moth/moth.pri13
-rw-r--r--src/qml/qml/v4/moth/qv4instr_moth.cpp56
-rw-r--r--src/qml/qml/v4/moth/qv4instr_moth_p.h612
-rw-r--r--src/qml/qml/v4/moth/qv4isel_moth.cpp1075
-rw-r--r--src/qml/qml/v4/moth/qv4isel_moth_p.h201
-rw-r--r--src/qml/qml/v4/moth/qv4vme_moth.cpp592
-rw-r--r--src/qml/qml/v4/moth/qv4vme_moth_p.h76
-rw-r--r--src/qml/qml/v4/qv4_llvm_p.h65
-rw-r--r--src/qml/qml/v4/qv4alloca_p.h54
-rw-r--r--src/qml/qml/v4/qv4argumentsobject.cpp171
-rw-r--r--src/qml/qml/v4/qv4argumentsobject_p.h97
-rw-r--r--src/qml/qml/v4/qv4arrayobject.cpp865
-rw-r--r--src/qml/qml/v4/qv4arrayobject_p.h100
-rw-r--r--src/qml/qml/v4/qv4bindings.cpp2478
-rw-r--r--src/qml/qml/v4/qv4bindings_p.h172
-rw-r--r--src/qml/qml/v4/qv4booleanobject.cpp97
-rw-r--r--src/qml/qml/v4/qv4booleanobject_p.h77
-rw-r--r--src/qml/qml/v4/qv4codegen.cpp2605
-rw-r--r--src/qml/qml/v4/qv4codegen_p.h451
-rw-r--r--src/qml/qml/v4/qv4compiler.cpp1580
-rw-r--r--src/qml/qml/v4/qv4compiler_p_p.h245
-rw-r--r--src/qml/qml/v4/qv4context.cpp615
-rw-r--r--src/qml/qml/v4/qv4context_p.h227
-rw-r--r--src/qml/qml/v4/qv4dateobject.cpp1316
-rw-r--r--src/qml/qml/v4/qv4dateobject_p.h137
-rw-r--r--src/qml/qml/v4/qv4debugging.cpp372
-rw-r--r--src/qml/qml/v4/qv4debugging_p.h161
-rw-r--r--src/qml/qml/v4/qv4engine.cpp837
-rw-r--r--src/qml/qml/v4/qv4engine_p.h332
-rw-r--r--src/qml/qml/v4/qv4errorobject.cpp351
-rw-r--r--src/qml/qml/v4/qv4errorobject_p.h246
-rw-r--r--src/qml/qml/v4/qv4exception.cpp129
-rw-r--r--src/qml/qml/v4/qv4exception_gcc.cpp143
-rw-r--r--src/qml/qml/v4/qv4exception_p.h81
-rw-r--r--src/qml/qml/v4/qv4executableallocator.cpp220
-rw-r--r--src/qml/qml/v4/qv4executableallocator_p.h134
-rw-r--r--src/qml/qml/v4/qv4function.cpp88
-rw-r--r--src/qml/qml/v4/qv4function_p.h142
-rw-r--r--src/qml/qml/v4/qv4functionobject.cpp558
-rw-r--r--src/qml/qml/v4/qv4functionobject_p.h223
-rw-r--r--src/qml/qml/v4/qv4global_p.h197
-rw-r--r--src/qml/qml/v4/qv4globalobject.cpp652
-rw-r--r--src/qml/qml/v4/qv4globalobject_p.h82
-rw-r--r--src/qml/qml/v4/qv4identifier.cpp172
-rw-r--r--src/qml/qml/v4/qv4identifier_p.h220
-rw-r--r--src/qml/qml/v4/qv4identifiertable.cpp184
-rw-r--r--src/qml/qml/v4/qv4identifiertable_p.h86
-rw-r--r--src/qml/qml/v4/qv4include.cpp232
-rw-r--r--src/qml/qml/v4/qv4include_p.h (renamed from src/qml/qml/v4/qv4program_p.h)107
-rw-r--r--src/qml/qml/v4/qv4instruction.cpp532
-rw-r--r--src/qml/qml/v4/qv4instruction_p.h483
-rw-r--r--src/qml/qml/v4/qv4internalclass.cpp296
-rw-r--r--src/qml/qml/v4/qv4internalclass_p.h154
-rw-r--r--src/qml/qml/v4/qv4ir.cpp919
-rw-r--r--src/qml/qml/v4/qv4ir_p.h615
-rw-r--r--src/qml/qml/v4/qv4irbuilder.cpp1394
-rw-r--r--src/qml/qml/v4/qv4irbuilder_p.h239
-rw-r--r--src/qml/qml/v4/qv4isel_llvm.cpp1388
-rw-r--r--src/qml/qml/v4/qv4isel_llvm_p.h178
-rw-r--r--src/qml/qml/v4/qv4isel_masm.cpp1450
-rw-r--r--src/qml/qml/v4/qv4isel_masm_p.h938
-rw-r--r--src/qml/qml/v4/qv4isel_p.cpp440
-rw-r--r--src/qml/qml/v4/qv4isel_p.h165
-rw-r--r--src/qml/qml/v4/qv4isel_util_p.h87
-rw-r--r--src/qml/qml/v4/qv4jsir.cpp1024
-rw-r--r--src/qml/qml/v4/qv4jsir_p.h890
-rw-r--r--src/qml/qml/v4/qv4jsonobject.cpp1040
-rw-r--r--src/qml/qml/v4/qv4jsonobject_p.h85
-rw-r--r--src/qml/qml/v4/qv4lookup.cpp365
-rw-r--r--src/qml/qml/v4/qv4lookup_p.h94
-rw-r--r--src/qml/qml/v4/qv4managed.cpp212
-rw-r--r--src/qml/qml/v4/qv4managed_p.h321
-rw-r--r--src/qml/qml/v4/qv4math_p.h147
-rw-r--r--src/qml/qml/v4/qv4mathobject.cpp311
-rw-r--r--src/qml/qml/v4/qv4mathobject_p.h78
-rw-r--r--src/qml/qml/v4/qv4mm.cpp605
-rw-r--r--src/qml/qml/v4/qv4mm_p.h157
-rw-r--r--src/qml/qml/v4/qv4numberobject.cpp257
-rw-r--r--src/qml/qml/v4/qv4numberobject_p.h81
-rw-r--r--src/qml/qml/v4/qv4object.cpp1383
-rw-r--r--src/qml/qml/v4/qv4object_p.h432
-rw-r--r--src/qml/qml/v4/qv4objectiterator.cpp129
-rw-r--r--src/qml/qml/v4/qv4objectiterator_p.h87
-rw-r--r--src/qml/qml/v4/qv4objectproto.cpp624
-rw-r--r--src/qml/qml/v4/qv4objectproto_p.h107
-rw-r--r--src/qml/qml/v4/qv4property_p.h150
-rw-r--r--src/qml/qml/v4/qv4qmlextensions.cpp51
-rw-r--r--src/qml/qml/v4/qv4qmlextensions_p.h66
-rw-r--r--src/qml/qml/v4/qv4qobjectwrapper.cpp1792
-rw-r--r--src/qml/qml/v4/qv4qobjectwrapper_p.h201
-rw-r--r--src/qml/qml/v4/qv4regexp.cpp180
-rw-r--r--src/qml/qml/v4/qv4regexp_p.h151
-rw-r--r--src/qml/qml/v4/qv4regexpobject.cpp359
-rw-r--r--src/qml/qml/v4/qv4regexpobject_p.h123
-rw-r--r--src/qml/qml/v4/qv4runtime.cpp1269
-rw-r--r--src/qml/qml/v4/qv4runtime_p.h717
-rw-r--r--src/qml/qml/v4/qv4script.cpp249
-rw-r--r--src/qml/qml/v4/qv4script_p.h88
-rw-r--r--src/qml/qml/v4/qv4sequenceobject.cpp651
-rw-r--r--src/qml/qml/v4/qv4sequenceobject_p.h91
-rw-r--r--src/qml/qml/v4/qv4serialize.cpp399
-rw-r--r--src/qml/qml/v4/qv4serialize_p.h (renamed from src/qml/qml/v4/qv4compiler_p.h)55
-rw-r--r--src/qml/qml/v4/qv4sparsearray.cpp459
-rw-r--r--src/qml/qml/v4/qv4sparsearray_p.h367
-rw-r--r--src/qml/qml/v4/qv4ssa.cpp2122
-rw-r--r--src/qml/qml/v4/qv4ssa_p.h127
-rw-r--r--src/qml/qml/v4/qv4stacktrace.cpp146
-rw-r--r--src/qml/qml/v4/qv4stacktrace_p.h75
-rw-r--r--src/qml/qml/v4/qv4string.cpp304
-rw-r--r--src/qml/qml/v4/qv4string_p.h144
-rw-r--r--src/qml/qml/v4/qv4stringobject.cpp761
-rw-r--r--src/qml/qml/v4/qv4stringobject_p.h110
-rw-r--r--src/qml/qml/v4/qv4syntaxchecker.cpp123
-rw-r--r--src/qml/qml/v4/qv4syntaxchecker_p.h77
-rw-r--r--src/qml/qml/v4/qv4unwindhelper.cpp82
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p-arm.h233
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p-dw2.h191
-rw-r--r--src/qml/qml/v4/qv4unwindhelper_p.h72
-rw-r--r--src/qml/qml/v4/qv4util_p.h74
-rw-r--r--src/qml/qml/v4/qv4value.cpp403
-rw-r--r--src/qml/qml/v4/qv4value_def_p.h280
-rw-r--r--src/qml/qml/v4/qv4value_p.h426
-rw-r--r--src/qml/qml/v4/qv4variantobject.cpp203
-rw-r--r--src/qml/qml/v4/qv4variantobject_p.h102
-rw-r--r--src/qml/qml/v4/v4.pri207
127 files changed, 43079 insertions, 8770 deletions
diff --git a/src/qml/qml/v4/llvm_installation.pri b/src/qml/qml/v4/llvm_installation.pri
new file mode 100644
index 0000000000..99e955fd2b
--- /dev/null
+++ b/src/qml/qml/v4/llvm_installation.pri
@@ -0,0 +1,23 @@
+LLVM_CONFIG=llvm-config
+# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that.
+isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR)
+!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config
+exists ($${LLVM_CONFIG}) {
+ CONFIG += llvm-libs
+ message("Found LLVM in $$LLVM_INSTALL_DIR")
+}
+
+llvm-libs {
+ win32 {
+ LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include
+# TODO: check if the next line is needed somehow for the llvm_runtime target.
+ LLVM_LIBS += -ladvapi32 -lshell32
+ }
+
+ unix {
+ LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir)
+ LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir)
+ }
+
+ LLVM_DEFINES += __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS
+}
diff --git a/src/qml/qml/v4/llvm_runtime.cpp b/src/qml/qml/v4/llvm_runtime.cpp
new file mode 100644
index 0000000000..0498bab44f
--- /dev/null
+++ b/src/qml/qml/v4/llvm_runtime.cpp
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4runtime_p.h"
+#include "qv4context_p.h"
+#include "qv4engine_p.h"
+#include <stdio.h>
+#include <setjmp.h>
+
+using namespace QV4;
+
+extern "C" {
+
+Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result)
+{
+ return *result;
+}
+
+Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index)
+{
+ assert(ctx->type == ExecutionContext::Type_CallContext);
+ return &static_cast<CallContext *>(ctx)->arguments[index];
+}
+
+void __qmljs_llvm_init_undefined(Value *result)
+{
+ *result = Value::undefinedValue();
+}
+
+void __qmljs_llvm_init_null(Value *result)
+{
+ *result = Value::nullValue();
+}
+
+void __qmljs_llvm_init_boolean(Value *result, bool value)
+{
+ *result = Value::fromBoolean(value);
+}
+
+void __qmljs_llvm_init_number(Value *result, double value)
+{
+ *result = Value::fromDouble(value);
+}
+
+void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str)
+{
+ *result = Value::fromString(ctx->engine->newString(QString::fromUtf8(str)));
+}
+
+void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result,
+ String *name, bool hasDirectEval,
+ bool usesArgumentsObject, bool isStrict,
+ bool hasNestedFunctions,
+ String **formals, unsigned formalCount,
+ String **locals, unsigned localCount)
+{
+ Function *clos = __qmljs_register_function(ctx, name, hasDirectEval,
+ usesArgumentsObject, isStrict,
+ hasNestedFunctions,
+ formals, formalCount,
+ locals, localCount);
+ __qmljs_init_closure(ctx, result, clos);
+}
+
+bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value)
+{
+ return __qmljs_to_boolean(*value);
+}
+
+void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_bit_and(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_bit_or(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_bit_xor(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_add(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_sub(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_mul(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_div(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_mod(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_shl(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_shr(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_ushr(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_gt(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_lt(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_ge(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_le(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_eq(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_ne(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_se(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_sne(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_instanceof(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ __qmljs_in(ctx, result, *left, *right);
+}
+
+void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ __qmljs_uplus(result, *value);
+}
+
+void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ __qmljs_uminus(result, *value);
+}
+
+void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ __qmljs_compl(result, *value);
+}
+
+void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ __qmljs_not(result, *value);
+}
+
+void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_and_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_or_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_xor_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_add_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_sub_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_mul_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_div_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_mod_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_shl_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_shr_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_ushr_name(ctx, dest, *src);
+}
+
+void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_and_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_or_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_xor_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_add_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_sub_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_mul_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_div_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_mod_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_shl_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_shr_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_ushr_element(ctx, *base, *index, *value);
+}
+
+void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_and_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_or_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_xor_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_add_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_sub_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_mul_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_div_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_mod_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_shl_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_shr_member(ctx, *base, member, *value);
+}
+
+void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_ushr_member(ctx, *base, member, *value);
+}
+
+String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str)
+{
+ return ctx->engine->newIdentifier(QString::fromUtf8(str));
+}
+
+void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ __qmljs_call_activation_property(context, result, name, args, argc);
+}
+
+void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc)
+{
+ __qmljs_call_value(context, result, thisObject, *func, args, argc);
+}
+
+void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ __qmljs_construct_activation_property(context, result, name, args, argc);
+}
+
+void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc)
+{
+ __qmljs_construct_value(context, result, *func, args, argc);
+}
+
+void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name)
+{
+ __qmljs_get_activation_property(ctx, result, name);
+}
+
+void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value)
+{
+ __qmljs_set_activation_property(ctx, name, *value);
+}
+
+void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name)
+{
+ __qmljs_get_property(ctx, result, *object, name);
+}
+
+void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc)
+{
+ __qmljs_call_property(context, result, *base, name, args, argc);
+}
+
+void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc)
+{
+ __qmljs_construct_property(context, result, *base, name, args, argc);
+}
+
+void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index)
+{
+ __qmljs_get_element(ctx, result, *object, *index);
+}
+
+void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value)
+{
+ __qmljs_set_element(ctx, *object, *index, *value);
+}
+
+void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value)
+{
+ __qmljs_set_property(ctx, *object, name, *value);
+}
+
+void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name)
+{
+ __qmljs_builtin_declare_var(ctx, deletable, name);
+}
+
+void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ __qmljs_builtin_typeof(ctx, result, *value);
+}
+
+void __qmljs_llvm_throw(ExecutionContext *context, Value *value)
+{
+ __qmljs_throw(context, *value);
+}
+
+void __qmljs_llvm_delete_exception_handler(ExecutionContext *context)
+{
+ // ### FIXME.
+}
+
+void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in)
+{
+ __qmljs_foreach_iterator_object(context, result, *in);
+}
+
+void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it)
+{
+ __qmljs_foreach_next_property_name(result, *it);
+}
+
+void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result)
+{
+ *result = ctx->thisObject;
+}
+
+void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index)
+{
+ __qmljs_delete_subscript(ctx, result, *base, *index);
+}
+
+void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name)
+{
+ __qmljs_delete_member(ctx, result, *base, name);
+}
+
+void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name)
+{
+ __qmljs_delete_name(ctx, result, name);
+}
+
+} // extern "C"
diff --git a/src/qml/qml/v4/moth/moth.pri b/src/qml/qml/v4/moth/moth.pri
new file mode 100644
index 0000000000..73bd893286
--- /dev/null
+++ b/src/qml/qml/v4/moth/moth.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qv4isel_moth_p.h \
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4vme_moth_p.h
+
+SOURCES += \
+ $$PWD/qv4isel_moth.cpp \
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4vme_moth.cpp
+
+#DEFINES += DO_TRACE_INSTR
diff --git a/src/qml/qml/v4/moth/qv4instr_moth.cpp b/src/qml/qml/v4/moth/qv4instr_moth.cpp
new file mode 100644
index 0000000000..ec68ede72d
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4instr_moth.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4instr_moth_p.h"
+
+using namespace QQmlJS;
+using namespace QQmlJS::Moth;
+
+int Instr::size(Type type)
+{
+#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size;
+ switch (type) {
+ FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
+ default: return 0;
+ }
+#undef MOTH_RETURN_INSTR_SIZE
+}
+
diff --git a/src/qml/qml/v4/moth/qv4instr_moth_p.h b/src/qml/qml/v4/moth/qv4instr_moth_p.h
new file mode 100644
index 0000000000..7397a1811d
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4instr_moth_p.h
@@ -0,0 +1,612 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4INSTR_MOTH_P_H
+#define QV4INSTR_MOTH_P_H
+
+#include <QtCore/qglobal.h>
+#include <private/qv4object_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define FOR_EACH_MOTH_INSTR(F) \
+ F(Ret, ret) \
+ F(LoadValue, loadValue) \
+ F(LoadClosure, loadClosure) \
+ F(MoveTemp, moveTemp) \
+ F(LoadName, loadName) \
+ F(StoreName, storeName) \
+ F(LoadElement, loadElement) \
+ F(StoreElement, storeElement) \
+ F(LoadProperty, loadProperty) \
+ F(StoreProperty, storeProperty) \
+ F(Push, push) \
+ F(EnterTry, enterTry) \
+ F(CallValue, callValue) \
+ F(CallProperty, callProperty) \
+ F(CallElement, callElement) \
+ F(CallActivationProperty, callActivationProperty) \
+ F(CallBuiltinThrow, callBuiltinThrow) \
+ F(CallBuiltinFinishTry, callBuiltinFinishTry) \
+ F(CallBuiltinPushScope, callBuiltinPushScope) \
+ F(CallBuiltinPopScope, callBuiltinPopScope) \
+ F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \
+ F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \
+ F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \
+ F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \
+ F(CallBuiltinDeleteName, callBuiltinDeleteName) \
+ F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \
+ F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \
+ F(CallBuiltinTypeofName, callBuiltinTypeofName) \
+ F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \
+ F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \
+ F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \
+ F(CallBuiltinPostIncName, callBuiltinPostIncName) \
+ F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \
+ F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \
+ F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \
+ F(CallBuiltinPostDecName, callBuiltinPostDecName) \
+ F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \
+ F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \
+ F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \
+ F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \
+ F(CallBuiltinDefineArray, callBuiltinDefineArray) \
+ F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \
+ F(CreateValue, createValue) \
+ F(CreateProperty, createProperty) \
+ F(CreateActivationProperty, createActivationProperty) \
+ F(Jump, jump) \
+ F(CJump, cjump) \
+ F(Unop, unop) \
+ F(Binop, binop) \
+ F(AddNumberParams, addNumberParams) \
+ F(MulNumberParams, mulNumberParams) \
+ F(SubNumberParams, subNumberParams) \
+ F(LoadThis, loadThis) \
+ F(InplaceElementOp, inplaceElementOp) \
+ F(InplaceMemberOp, inplaceMemberOp) \
+ F(InplaceNameOp, inplaceNameOp)
+
+#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200)
+# define MOTH_THREADED_INTERPRETER
+#endif
+
+#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1)
+
+#ifdef MOTH_THREADED_INTERPRETER
+# define MOTH_INSTR_HEADER void *code; \
+ unsigned int breakPoint : 1;
+#else
+# define MOTH_INSTR_HEADER quint8 instructionType; \
+ unsigned int breakPoint : 1;
+#endif
+
+#define MOTH_INSTR_ENUM(I, FMT) I,
+#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK)
+
+
+namespace QQmlJS {
+namespace Moth {
+
+union Instr
+{
+ struct Param {
+ enum {
+ ValueType = 0,
+ ArgumentType = 1,
+ LocalType = 2,
+ TempType = 3,
+ ScopedLocalType = 4
+ };
+ QV4::Value value;
+ unsigned type : 3;
+ unsigned scope : 29;
+ unsigned index;
+
+ bool isValue() const { return type == ValueType; }
+ bool isArgument() const { return type == ArgumentType; }
+ bool isLocal() const { return type == LocalType; }
+ bool isTemp() const { return type == TempType; }
+ bool isScopedLocal() const { return type == ScopedLocalType; }
+
+ static Param createValue(const QV4::Value &v)
+ {
+ Param p;
+ p.type = ValueType;
+ p.scope = 0;
+ p.value = v;
+ return p;
+ }
+
+ static Param createArgument(unsigned idx, uint scope)
+ {
+ Param p;
+ p.type = ArgumentType;
+ p.scope = scope;
+ p.index = idx;
+ return p;
+ }
+
+ static Param createLocal(unsigned idx)
+ {
+ Param p;
+ p.type = LocalType;
+ p.scope = 0;
+ p.index = idx;
+ return p;
+ }
+
+ static Param createTemp(unsigned idx)
+ {
+ Param p;
+ p.type = TempType;
+ p.scope = 0;
+ p.index = idx;
+ return p;
+ }
+
+ static Param createScopedLocal(unsigned idx, uint scope)
+ {
+ Param p;
+ p.type = ScopedLocalType;
+ p.scope = scope;
+ p.index = idx;
+ return p;
+ }
+
+ inline bool operator==(const Param &other) const
+ { return type == other.type && scope == other.scope && index == other.index; }
+
+ inline bool operator!=(const Param &other) const
+ { return !(*this == other); }
+ };
+
+ enum Type {
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ };
+
+ struct instr_common {
+ MOTH_INSTR_HEADER
+ };
+ struct instr_ret {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ struct instr_loadValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_moveTemp {
+ MOTH_INSTR_HEADER
+ Param source;
+ Param result;
+ };
+ struct instr_loadClosure {
+ MOTH_INSTR_HEADER
+ QV4::Function *value;
+ Param result;
+ };
+ struct instr_loadName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param result;
+ };
+ struct instr_storeName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param source;
+ };
+ struct instr_loadProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param base;
+ Param result;
+ };
+ struct instr_storeProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param base;
+ Param source;
+ };
+ struct instr_loadElement {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_storeElement {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param source;
+ };
+ struct instr_push {
+ MOTH_INSTR_HEADER
+ quint32 value;
+ };
+ struct instr_enterTry {
+ MOTH_INSTR_HEADER
+ ptrdiff_t tryOffset;
+ ptrdiff_t catchOffset;
+ QV4::String *exceptionVarName;
+ Param exceptionVar;
+ };
+ struct instr_callValue {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param dest;
+ Param result;
+ };
+ struct instr_callProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ quint32 argc;
+ quint32 args;
+ Param base;
+ Param result;
+ };
+ struct instr_callElement {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ quint32 argc;
+ quint32 args;
+ Param result;
+ };
+ struct instr_callActivationProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ quint32 argc;
+ quint32 args;
+ Param result;
+ };
+ struct instr_callBuiltinThrow {
+ MOTH_INSTR_HEADER
+ Param arg;
+ };
+ struct instr_callBuiltinFinishTry {
+ MOTH_INSTR_HEADER
+ };
+ 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
+ QV4::String *member;
+ Param base;
+ Param result;
+ };
+ struct instr_callBuiltinDeleteSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinDeleteName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofMember {
+ MOTH_INSTR_HEADER
+ QV4::String *member;
+ Param base;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncMember {
+ MOTH_INSTR_HEADER
+ Param base;
+ QV4::String *member;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecMember {
+ MOTH_INSTR_HEADER
+ Param base;
+ QV4::String *member;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecName {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinDeclareVar {
+ MOTH_INSTR_HEADER
+ QV4::String *varName;
+ bool isDeletable;
+ };
+ struct instr_callBuiltinDefineGetterSetter {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param object;
+ Param getter;
+ Param setter;
+ };
+ struct instr_callBuiltinDefineProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ Param object;
+ Param value;
+ };
+ struct instr_callBuiltinDefineArray {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param result;
+ };
+ struct instr_callBuiltinDefineObjectLiteral {
+ MOTH_INSTR_HEADER
+ QV4::InternalClass *internalClass;
+ quint32 args;
+ Param result;
+ };
+ struct instr_createValue {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param func;
+ Param result;
+ };
+ struct instr_createProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ quint32 argc;
+ quint32 args;
+ Param base;
+ Param result;
+ };
+ struct instr_createActivationProperty {
+ MOTH_INSTR_HEADER
+ QV4::String *name;
+ quint32 argc;
+ quint32 args;
+ Param result;
+ };
+ struct instr_jump {
+ MOTH_INSTR_HEADER
+ ptrdiff_t offset;
+ };
+ struct instr_cjump {
+ MOTH_INSTR_HEADER
+ ptrdiff_t offset;
+ Param condition;
+ };
+ struct instr_unop {
+ MOTH_INSTR_HEADER
+ QV4::UnaryOpName alu;
+ Param source;
+ Param result;
+ };
+ struct instr_binop {
+ MOTH_INSTR_HEADER
+ QV4::BinOp alu;
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_addNumberParams {
+ MOTH_INSTR_HEADER
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_mulNumberParams {
+ MOTH_INSTR_HEADER
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_subNumberParams {
+ MOTH_INSTR_HEADER
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_loadThis {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ struct instr_inplaceElementOp {
+ MOTH_INSTR_HEADER
+ QV4::InplaceBinOpElement alu;
+ Param base;
+ Param index;
+ Param source;
+ };
+ struct instr_inplaceMemberOp {
+ MOTH_INSTR_HEADER
+ QV4::InplaceBinOpMember alu;
+ QV4::String *member;
+ Param base;
+ Param source;
+ };
+ struct instr_inplaceNameOp {
+ MOTH_INSTR_HEADER
+ QV4::InplaceBinOpName alu;
+ QV4::String *name;
+ Param source;
+ };
+
+ instr_common common;
+ instr_ret ret;
+ instr_loadValue loadValue;
+ instr_moveTemp moveTemp;
+ instr_loadClosure loadClosure;
+ instr_loadName loadName;
+ instr_storeName storeName;
+ instr_loadElement loadElement;
+ instr_storeElement storeElement;
+ instr_loadProperty loadProperty;
+ instr_storeProperty storeProperty;
+ instr_push push;
+ instr_enterTry enterTry;
+ instr_callValue callValue;
+ instr_callProperty callProperty;
+ instr_callElement callElement;
+ instr_callActivationProperty callActivationProperty;
+ instr_callBuiltinThrow callBuiltinThrow;
+ instr_callBuiltinFinishTry callBuiltinFinishTry;
+ instr_callBuiltinPushScope callBuiltinPushScope;
+ instr_callBuiltinPopScope callBuiltinPopScope;
+ instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject;
+ instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName;
+ instr_callBuiltinDeleteMember callBuiltinDeleteMember;
+ instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript;
+ instr_callBuiltinDeleteName callBuiltinDeleteName;
+ instr_callBuiltinTypeofMember callBuiltinTypeofMember;
+ instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript;
+ instr_callBuiltinTypeofName callBuiltinTypeofName;
+ instr_callBuiltinTypeofValue callBuiltinTypeofValue;
+ instr_callBuiltinPostIncMember callBuiltinPostIncMember;
+ instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript;
+ instr_callBuiltinPostIncName callBuiltinPostIncName;
+ instr_callBuiltinPostIncValue callBuiltinPostIncValue;
+ instr_callBuiltinPostDecMember callBuiltinPostDecMember;
+ instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript;
+ instr_callBuiltinPostDecName callBuiltinPostDecName;
+ instr_callBuiltinPostDecValue callBuiltinPostDecValue;
+ instr_callBuiltinDeclareVar callBuiltinDeclareVar;
+ instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter;
+ instr_callBuiltinDefineProperty callBuiltinDefineProperty;
+ instr_callBuiltinDefineArray callBuiltinDefineArray;
+ instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral;
+ instr_createValue createValue;
+ instr_createProperty createProperty;
+ instr_createActivationProperty createActivationProperty;
+ instr_jump jump;
+ instr_cjump cjump;
+ instr_unop unop;
+ instr_binop binop;
+ instr_addNumberParams addNumberParams;
+ instr_mulNumberParams mulNumberParams;
+ instr_subNumberParams subNumberParams;
+ instr_loadThis loadThis;
+ instr_inplaceElementOp inplaceElementOp;
+ instr_inplaceMemberOp inplaceMemberOp;
+ instr_inplaceNameOp inplaceNameOp;
+
+ static int size(Type type);
+};
+
+template<int N>
+struct InstrMeta {
+};
+
+#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; } \
+ };
+FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
+#undef MOTH_INSTR_META_TEMPLATE
+
+template<int InstrType>
+class InstrData : public InstrMeta<InstrType>::DataType
+{
+};
+
+} // namespace Moth
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4INSTR_MOTH_P_H
diff --git a/src/qml/qml/v4/moth/qv4isel_moth.cpp b/src/qml/qml/v4/moth/qv4isel_moth.cpp
new file mode 100644
index 0000000000..04d759ed8d
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4isel_moth.cpp
@@ -0,0 +1,1075 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4isel_util_p.h"
+#include "qv4isel_moth_p.h"
+#include "qv4vme_moth_p.h"
+#include "qv4ssa_p.h"
+#include <private/qv4functionobject_p.h>
+#include <private/qv4regexpobject_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv4function_p.h>
+
+#undef USE_TYPE_INFO
+
+using namespace QQmlJS;
+using namespace QQmlJS::Moth;
+
+namespace {
+
+inline QV4::BinOp aluOpFunction(V4IR::AluOp op)
+{
+ switch (op) {
+ case V4IR::OpInvalid:
+ return 0;
+ case V4IR::OpIfTrue:
+ return 0;
+ case V4IR::OpNot:
+ return 0;
+ case V4IR::OpUMinus:
+ return 0;
+ case V4IR::OpUPlus:
+ return 0;
+ case V4IR::OpCompl:
+ return 0;
+ case V4IR::OpBitAnd:
+ return QV4::__qmljs_bit_and;
+ case V4IR::OpBitOr:
+ return QV4::__qmljs_bit_or;
+ case V4IR::OpBitXor:
+ return QV4::__qmljs_bit_xor;
+ case V4IR::OpAdd:
+ return QV4::__qmljs_add;
+ case V4IR::OpSub:
+ return QV4::__qmljs_sub;
+ case V4IR::OpMul:
+ return QV4::__qmljs_mul;
+ case V4IR::OpDiv:
+ return QV4::__qmljs_div;
+ case V4IR::OpMod:
+ return QV4::__qmljs_mod;
+ case V4IR::OpLShift:
+ return QV4::__qmljs_shl;
+ case V4IR::OpRShift:
+ return QV4::__qmljs_shr;
+ case V4IR::OpURShift:
+ return QV4::__qmljs_ushr;
+ case V4IR::OpGt:
+ return QV4::__qmljs_gt;
+ case V4IR::OpLt:
+ return QV4::__qmljs_lt;
+ case V4IR::OpGe:
+ return QV4::__qmljs_ge;
+ case V4IR::OpLe:
+ return QV4::__qmljs_le;
+ case V4IR::OpEqual:
+ return QV4::__qmljs_eq;
+ case V4IR::OpNotEqual:
+ return QV4::__qmljs_ne;
+ case V4IR::OpStrictEqual:
+ return QV4::__qmljs_se;
+ case V4IR::OpStrictNotEqual:
+ return QV4::__qmljs_sne;
+ case V4IR::OpInstanceof:
+ return QV4::__qmljs_instanceof;
+ case V4IR::OpIn:
+ return QV4::__qmljs_in;
+ case V4IR::OpAnd:
+ return 0;
+ case V4IR::OpOr:
+ return 0;
+ default:
+ assert(!"Unknown AluOp");
+ return 0;
+ }
+};
+} // anonymous namespace
+
+// TODO: extend to optimize out temp-to-temp moves, where the lifetime of one temp ends at that statement.
+// To handle that, add a hint when such a move will occur, and add a stmt for the hint.
+// Then when asked for a register, check if the active statement is the terminating statement, and if so, apply the hint.
+// This generalises the hint usage for Phi removal too, when the phi is passed in there as the current statement.
+class QQmlJS::Moth::StackSlotAllocator
+{
+ QHash<V4IR::Temp, int> _slotForTemp;
+ QHash<V4IR::Temp, int> _hints;
+ QVector<int> _activeSlots;
+
+ QHash<V4IR::Temp, V4IR::LifeTimeInterval> _intervals;
+
+public:
+ StackSlotAllocator(const QList<V4IR::LifeTimeInterval> &ranges, int maxTempCount)
+ : _activeSlots(maxTempCount)
+ {
+ _intervals.reserve(ranges.size());
+ foreach (const V4IR::LifeTimeInterval &r, ranges)
+ _intervals[r.temp()] = r;
+ }
+
+ void addHint(const V4IR::Temp &hintedSlotOfTemp, const V4IR::Temp &newTemp)
+ {
+ if (hintedSlotOfTemp.kind != V4IR::Temp::VirtualRegister
+ || newTemp.kind != V4IR::Temp::VirtualRegister)
+ return;
+
+ if (_slotForTemp.contains(newTemp) || _hints.contains(newTemp))
+ return;
+
+ int hintedSlot = _slotForTemp.value(hintedSlotOfTemp, -1);
+ Q_ASSERT(hintedSlot >= 0);
+ _hints[newTemp] = hintedSlot;
+ }
+
+ int stackSlotFor(V4IR::Temp *t, V4IR::Stmt *currentStmt) {
+ int idx = _slotForTemp.value(*t, -1);
+ if (idx == -1)
+ idx = allocateSlot(t, currentStmt);
+ Q_ASSERT(idx >= 0);
+ return idx;
+ }
+
+private:
+ int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) {
+ const V4IR::LifeTimeInterval &interval = _intervals[*t];
+ int idx = _hints.value(*t, -1);
+ if (idx != -1 && _activeSlots[idx] <= currentStmt->id) {
+ _slotForTemp[*t] = idx;
+ _activeSlots[idx] = interval.end();
+ return idx;
+ }
+
+ for (int i = 0, ei = _activeSlots.size(); i != ei; ++i) {
+ if (_activeSlots[i] < currentStmt->id) {
+ _slotForTemp[*t] = i;
+ _activeSlots[i] = interval.end();
+ return i;
+ }
+ }
+
+ return -1;
+ }
+};
+
+InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module)
+ : EvalInstructionSelection(engine, module)
+ , _function(0)
+ , _vmFunction(0)
+ , _block(0)
+ , _codeStart(0)
+ , _codeNext(0)
+ , _codeEnd(0)
+ , _stackSlotAllocator(0)
+ , _currentStatement(0)
+{
+}
+
+InstructionSelection::~InstructionSelection()
+{
+}
+
+void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function)
+{
+ V4IR::BasicBlock *block = 0, *nextBlock = 0;
+
+ QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > patches;
+ QHash<V4IR::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(_vmFunction, vmFunction);
+ qSwap(block, _block);
+ qSwap(nextBlock, _nextBlock);
+ qSwap(patches, _patches);
+ qSwap(addrs, _addrs);
+ qSwap(codeStart, _codeStart);
+ qSwap(codeNext, _codeNext);
+ qSwap(codeEnd, _codeEnd);
+
+ V4IR::Optimizer opt(_function);
+ opt.run();
+ StackSlotAllocator *stackSlotAllocator = 0;
+ if (opt.isInSSA())
+ stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount);
+ qSwap(_stackSlotAllocator, stackSlotAllocator);
+ V4IR::Stmt *cs = 0;
+ qSwap(_currentStatement, cs);
+
+ int locals = frameSize();
+ assert(locals >= 0);
+
+ Instruction::Push push;
+ push.value = quint32(locals);
+ addInstruction(push);
+
+ for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) {
+ _block = _function->basicBlocks[i];
+ _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0;
+ _addrs.insert(_block, _codeNext - _codeStart);
+
+ foreach (V4IR::Stmt *s, _block->statements) {
+ if (s->location.isValid()) {
+ QV4::LineNumberMapping mapping;
+ mapping.codeOffset = _codeNext - _codeStart;
+ mapping.lineNumber = s->location.startLine;
+ _vmFunction->lineNumberMappings.append(mapping);
+ }
+
+ if (opt.isInSSA() && s->asTerminator()) {
+ foreach (const V4IR::Optimizer::SSADeconstructionMove &move,
+ opt.ssaDeconstructionMoves(_block)) {
+ Q_ASSERT(move.source->asTemp()); // FIXME: support Const exprs in Phi nodes.
+ if (move.needsConversion())
+ convertType(move.source->asTemp(), move.target);
+ else
+ copyValue(move.source->asTemp(), move.target);
+ }
+ }
+
+ _currentStatement = s;
+ s->accept(this);
+ }
+ }
+
+ // TODO: patch stack size (the push instruction)
+ patchJumpAddresses();
+
+ _vmFunction->code = VME::exec;
+ _vmFunction->codeData = squeezeCode();
+
+ if (QV4::Debugging::Debugger *debugger = engine()->debugger)
+ debugger->setPendingBreakpoints(_vmFunction);
+
+ qSwap(_currentStatement, cs);
+ qSwap(_stackSlotAllocator, stackSlotAllocator);
+ delete stackSlotAllocator;
+ qSwap(_function, function);
+ qSwap(_vmFunction, vmFunction);
+ qSwap(block, _block);
+ qSwap(nextBlock, _nextBlock);
+ qSwap(patches, _patches);
+ qSwap(addrs, _addrs);
+ qSwap(codeStart, _codeStart);
+ qSwap(codeNext, _codeNext);
+ qSwap(codeEnd, _codeEnd);
+
+ delete[] codeStart;
+}
+
+void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ Instruction::CallValue call;
+ prepareCallArgs(args, call.argc, call.args);
+ call.dest = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // call the property on the loaded base
+ Instruction::CallProperty call;
+ call.base = getParam(base);
+ call.name = identifier(name);
+ prepareCallArgs(args, call.argc, call.args);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // call the property on the loaded base
+ Instruction::CallElement call;
+ call.base = getParam(base);
+ call.index = getParam(index);
+ prepareCallArgs(args, call.argc, call.args);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target)
+{
+ if (_stackSlotAllocator)
+ _stackSlotAllocator->addHint(*source, *target);
+
+ // FIXME: do something more useful with this info
+ copyValue(source, target);
+}
+
+void InstructionSelection::constructActivationProperty(V4IR::Name *func,
+ V4IR::ExprList *args,
+ V4IR::Temp *result)
+{
+ Instruction::CreateActivationProperty create;
+ create.name = identifier(*func->id);
+ prepareCallArgs(args, create.argc, create.args);
+ create.result = getResultParam(result);
+ addInstruction(create);
+}
+
+void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ Instruction::CreateProperty create;
+ create.base = getParam(base);
+ create.name = identifier(name);
+ prepareCallArgs(args, create.argc, create.args);
+ create.result = getResultParam(result);
+ addInstruction(create);
+}
+
+void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ Instruction::CreateValue create;
+ create.func = getParam(value);
+ prepareCallArgs(args, create.argc, create.args);
+ create.result = getResultParam(result);
+ addInstruction(create);
+}
+
+void InstructionSelection::loadThisObject(V4IR::Temp *temp)
+{
+ Instruction::LoadThis load;
+ load.result = getResultParam(temp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp)
+{
+ assert(sourceConst);
+
+ Instruction::LoadValue load;
+ load.value = getParam(sourceConst);
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp)
+{
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(QV4::Value::fromString(identifier(str)));
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp)
+{
+ QV4::Value v = QV4::Value::fromObject(engine()->newRegExpObject(
+ *sourceRegexp->value,
+ sourceRegexp->flags));
+ _vmFunction->generatedValues.append(v);
+
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(v);
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp)
+{
+ Instruction::LoadName load;
+ load.name = identifier(*name->id);
+ load.result = getResultParam(temp);
+ addInstruction(load);
+}
+
+void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName)
+{
+ Instruction::StoreName store;
+ store.source = getParam(source);
+ store.name = identifier(targetName);
+ addInstruction(store);
+}
+
+void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target)
+{
+ QV4::Function *vmFunc = vmFunction(closure->value);
+ assert(vmFunc);
+ Instruction::LoadClosure load;
+ load.value = vmFunc;
+ load.result = getResultParam(target);
+ addInstruction(load);
+}
+
+void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target)
+{
+ Instruction::LoadProperty load;
+ load.base = getParam(base);
+ load.name = identifier(name);
+ load.result = getResultParam(target);
+ addInstruction(load);
+}
+
+void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ Instruction::StoreProperty store;
+ store.base = getParam(targetBase);
+ store.name = identifier(targetName);
+ store.source = getParam(source);
+ addInstruction(store);
+}
+
+void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target)
+{
+ Instruction::LoadElement load;
+ load.base = getParam(base);
+ load.index = getParam(index);
+ load.result = getResultParam(target);
+ addInstruction(load);
+}
+
+void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex)
+{
+ Instruction::StoreElement store;
+ store.base = getParam(targetBase);
+ store.index = getParam(targetIndex);
+ store.source = getParam(source);
+ addInstruction(store);
+}
+
+void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ if (_stackSlotAllocator)
+ _stackSlotAllocator->addHint(*sourceTemp, *targetTemp);
+
+ Instruction::MoveTemp move;
+ move.source = getParam(sourceTemp);
+ move.result = getResultParam(targetTemp);
+ if (move.source != move.result)
+ addInstruction(move);
+}
+
+void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ QV4::UnaryOpName op = 0;
+ switch (oper) {
+ case V4IR::OpIfTrue: assert(!"unreachable"); break;
+ case V4IR::OpNot: op = QV4::__qmljs_not; break;
+ case V4IR::OpUMinus: op = QV4::__qmljs_uminus; break;
+ case V4IR::OpUPlus: op = QV4::__qmljs_uplus; break;
+ case V4IR::OpCompl: op = QV4::__qmljs_compl; break;
+ case V4IR::OpIncrement: op = QV4::__qmljs_increment; break;
+ case V4IR::OpDecrement: op = QV4::__qmljs_decrement; break;
+ default: assert(!"unreachable"); break;
+ } // switch
+
+ if (op) {
+ Instruction::Unop unop;
+ unop.alu = op;
+ unop.source = getParam(sourceTemp);
+ unop.result = getResultParam(targetTemp);
+ addInstruction(unop);
+ } else {
+ qWarning(" UNOP1");
+ }
+}
+
+void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
+{
+#ifdef USE_TYPE_INFO
+ if (leftSource->type & V4IR::NumberType && rightSource->type & V4IR::NumberType) {
+ // TODO: add Temp+Const variation on the topic.
+ switch (oper) {
+ case V4IR::OpAdd: {
+ Instruction::AddNumberParams instr;
+ instr.lhs = getParam(leftSource);
+ instr.rhs = getParam(rightSource);
+ instr.result = getResultParam(target);
+ addInstruction(instr);
+ } return;
+ case V4IR::OpMul: {
+ Instruction::MulNumberParams instr;
+ instr.lhs = getParam(leftSource);
+ instr.rhs = getParam(rightSource);
+ instr.result = getResultParam(target);
+ addInstruction(instr);
+ } return;
+ case V4IR::OpSub: {
+ Instruction::SubNumberParams instr;
+ instr.lhs = getParam(leftSource);
+ instr.rhs = getParam(rightSource);
+ instr.result = getResultParam(target);
+ addInstruction(instr);
+ } return;
+ default:
+ break;
+ }
+ }
+#else // !USE_TYPE_INFO
+ Q_ASSERT(leftSource->asTemp() && rightSource->asTemp());
+#endif // USE_TYPE_INFO
+
+ Instruction::Binop binop;
+ binop.alu = aluOpFunction(oper);
+ binop.lhs = getParam(leftSource);
+ binop.rhs = getParam(rightSource);
+ binop.result = getResultParam(target);
+ addInstruction(binop);
+}
+
+void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName)
+{
+ QV4::InplaceBinOpName op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_name; break;
+ case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_name; break;
+ case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_name; break;
+ case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_name; break;
+ case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_name; break;
+ case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_name; break;
+ case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_name; break;
+ case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_name; break;
+ case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_name; break;
+ case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_name; break;
+ case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_name; break;
+ default: break;
+ }
+
+ if (op) {
+ Instruction::InplaceNameOp ieo;
+ ieo.alu = op;
+ ieo.name = identifier(targetName);
+ ieo.source = getParam(rightSource);
+ addInstruction(ieo);
+ }
+}
+
+void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp)
+{
+ QV4::InplaceBinOpElement op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_element; break;
+ case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_element; break;
+ case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_element; break;
+ case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_element; break;
+ case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_element; break;
+ case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_element; break;
+ case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_element; break;
+ case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_element; break;
+ case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_element; break;
+ case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_element; break;
+ case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_element; break;
+ default: break;
+ }
+
+ Instruction::InplaceElementOp ieo;
+ ieo.alu = op;
+ ieo.base = getParam(targetBaseTemp);
+ ieo.index = getParam(targetIndexTemp);
+ ieo.source = getParam(source);
+ addInstruction(ieo);
+}
+
+void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ QV4::InplaceBinOpMember op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_member; break;
+ case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_member; break;
+ case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_member; break;
+ case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_member; break;
+ case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_member; break;
+ case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_member; break;
+ case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_member; break;
+ case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_member; break;
+ case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_member; break;
+ case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_member; break;
+ case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_member; break;
+ default: break;
+ }
+
+ Instruction::InplaceMemberOp imo;
+ imo.alu = op;
+ imo.base = getParam(targetBase);
+ imo.member = identifier(targetName);
+ imo.source = getParam(source);
+ addInstruction(imo);
+}
+
+void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args)
+{
+ bool singleArgIsTemp = false;
+ if (e && e->next == 0 && e->expr->asTemp()) {
+ singleArgIsTemp = e->expr->asTemp()->kind == V4IR::Temp::VirtualRegister;
+ }
+
+ if (singleArgIsTemp) {
+ // We pass single arguments as references to the stack, but only if it's not a local or an argument.
+ argc = 1;
+ args = getParam(e->expr).index;
+ } else if (e) {
+ // We need to move all the temps into the function arg array
+ int argLocation = outgoingArgumentTempStart();
+ assert(argLocation >= 0);
+ argc = 0;
+ args = argLocation;
+ while (e) {
+ Instruction::MoveTemp move;
+ move.source = getParam(e->expr);
+ move.result = Instr::Param::createTemp(argLocation);
+ addInstruction(move);
+ ++argLocation;
+ ++argc;
+ e = e->next;
+ }
+ } else {
+ argc = 0;
+ args = 0;
+ }
+}
+
+void InstructionSelection::visitJump(V4IR::Jump *s)
+{
+ if (s->target == _nextBlock)
+ return;
+
+ 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(V4IR::CJump *s)
+{
+ Instr::Param condition;
+ if (V4IR::Temp *t = s->cond->asTemp()) {
+ condition = getResultParam(t);
+ } else if (V4IR::Binop *b = s->cond->asBinop()) {
+ condition = getResultParam(0);
+ Instruction::Binop binop;
+ binop.alu = aluOpFunction(b->op);
+ binop.lhs = getParam(b->left);
+ binop.rhs = getParam(b->right);
+ binop.result = condition;
+ addInstruction(binop);
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+
+ Instruction::CJump 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(V4IR::Ret *s)
+{
+ Instruction::Ret ret;
+ ret.result = getParam(s->expr);
+ addInstruction(ret);
+}
+
+void InstructionSelection::visitTry(V4IR::Try *t)
+{
+ Instruction::EnterTry enterTry;
+ enterTry.tryOffset = 0;
+ enterTry.catchOffset = 0;
+ enterTry.exceptionVarName = identifier(t->exceptionVarName);
+ enterTry.exceptionVar = getParam(t->exceptionVar);
+ ptrdiff_t enterTryLoc = addInstruction(enterTry);
+
+ ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry));
+ _patches[t->tryBlock].append(tryLoc);
+
+ ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry));
+ _patches[t->catchBlock].append(catchLoc);
+}
+
+void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ Instruction::CallActivationProperty call;
+ call.name = identifier(*func->id);
+ prepareCallArgs(args, call.argc, call.args);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofSubscript call;
+ call.base = getParam(base);
+ call.index = getParam(index);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinDeleteMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinDeleteSubscript call;
+ call.base = getParam(base);
+ call.index = getParam(index);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinDeleteName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
+{
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(QV4::Value::fromBoolean(false));
+ load.result = getResultParam(result);
+ addInstruction(load);
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecSubscript call;
+ call.base = getParam(base);
+ call.index = getParam(index);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncSubscript call;
+ call.base = getParam(base);
+ call.index = getParam(index);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg)
+{
+ Instruction::CallBuiltinThrow call;
+ call.arg = getParam(arg);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinFinishTry()
+{
+ Instruction::CallBuiltinFinishTry call;
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinForeachIteratorObject call;
+ call.arg = getParam(arg);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ Instruction::CallBuiltinForeachNextPropertyName call;
+ call.arg = getParam(arg);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg)
+{
+ Instruction::CallBuiltinPushScope call;
+ call.arg = getParam(arg);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPopScope()
+{
+ Instruction::CallBuiltinPopScope call;
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
+{
+ Instruction::CallBuiltinDeclareVar call;
+ call.isDeletable = deletable;
+ call.varName = identifier(name);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter)
+{
+ Instruction::CallBuiltinDefineGetterSetter call;
+ call.object = getParam(object);
+ call.name = identifier(name);
+ call.getter = getParam(getter);
+ call.setter = getParam(setter);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value)
+{
+ Instruction::CallBuiltinDefineProperty call;
+ call.object = getParam(object);
+ call.name = identifier(name);
+ call.value = getParam(value);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ Instruction::CallBuiltinDefineArray call;
+ prepareCallArgs(args, call.argc, call.args);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ int argLocation = outgoingArgumentTempStart();
+
+ QV4::InternalClass *klass = engine()->emptyClass;
+ V4IR::ExprList *it = args;
+ while (it) {
+ V4IR::Name *name = it->expr->asName();
+ it = it->next;
+
+ bool isData = it->expr->asConst()->value;
+ it = it->next;
+ klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
+
+ Instruction::MoveTemp move;
+ move.source = getParam(it->expr);
+ move.result = Instr::Param::createTemp(argLocation);
+ addInstruction(move);
+ ++argLocation;
+
+ if (!isData) {
+ it = it->next;
+
+ Instruction::MoveTemp move;
+ move.source = getParam(it->expr);
+ move.result = Instr::Param::createTemp(argLocation);
+ addInstruction(move);
+ ++argLocation;
+ }
+
+ it = it->next;
+ }
+
+ Instruction::CallBuiltinDefineObjectLiteral call;
+ call.internalClass = klass;
+ call.args = outgoingArgumentTempStart();
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
+{
+#ifdef MOTH_THREADED_INTERPRETER
+ instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)];
+#else
+ instr.common.instructionType = type;
+#endif
+ instr.common.breakPoint = 0;
+
+ 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<V4IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt;
+ for (PatchIt i = _patches.begin(), ei = _patches.end(); 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();
+}
+
+uchar *InstructionSelection::squeezeCode() const
+{
+ int codeSize = _codeNext - _codeStart;
+ uchar *squeezed = new uchar[codeSize];
+ ::memcpy(squeezed, _codeStart, codeSize);
+ return squeezed;
+}
+
+QV4::String *InstructionSelection::identifier(const QString &s)
+{
+ QV4::String *str = engine()->newIdentifier(s);
+ _vmFunction->identifiers.append(str);
+ return str;
+}
+
+Instr::Param InstructionSelection::getParam(V4IR::Expr *e) {
+ typedef Instr::Param Param;
+ assert(e);
+
+ if (V4IR::Const *c = e->asConst()) {
+ return Param::createValue(convertToValue(c));
+ } else if (V4IR::Temp *t = e->asTemp()) {
+ switch (t->kind) {
+ case V4IR::Temp::Formal:
+ case V4IR::Temp::ScopedFormal: return Param::createArgument(t->index, t->scope);
+ case V4IR::Temp::Local: return Param::createLocal(t->index);
+ case V4IR::Temp::ScopedLocal: return Param::createScopedLocal(t->index, t->scope);
+ case V4IR::Temp::VirtualRegister:
+ return Param::createTemp(_stackSlotAllocator ?
+ _stackSlotAllocator->stackSlotFor(t, _currentStatement) : t->index);
+ default:
+ Q_UNIMPLEMENTED();
+ return Param();
+ }
+ } else {
+ Q_UNIMPLEMENTED();
+ return Param();
+ }
+}
diff --git a/src/qml/qml/v4/moth/qv4isel_moth_p.h b/src/qml/qml/v4/moth/qv4isel_moth_p.h
new file mode 100644
index 0000000000..9c17245f67
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4isel_moth_p.h
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ISEL_MOTH_P_H
+#define QV4ISEL_MOTH_P_H
+
+#include <private/qv4global_p.h>
+#include <private/qv4isel_p.h>
+#include <private/qv4isel_util_p.h>
+#include <private/qv4jsir_p.h>
+#include <private/qv4object_p.h>
+#include "qv4instr_moth_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Moth {
+
+class StackSlotAllocator;
+
+class Q_QML_EXPORT InstructionSelection:
+ public V4IR::IRDecoder,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(QV4::Function *vmFunction, V4IR::Function *function);
+
+protected:
+ virtual void visitJump(V4IR::Jump *);
+ virtual void visitCJump(V4IR::CJump *);
+ virtual void visitRet(V4IR::Ret *);
+ virtual void visitTry(V4IR::Try *);
+
+ virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteValue(V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinThrow(V4IR::Temp *arg);
+ virtual void callBuiltinFinishTry();
+ virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinPushWithScope(V4IR::Temp *arg);
+ virtual void callBuiltinPopScope();
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name);
+ virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter);
+ virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value);
+ virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void convertType(V4IR::Temp *source, V4IR::Temp *target);
+ virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void loadThisObject(V4IR::Temp *temp);
+ virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp);
+ virtual void loadString(const QString &str, V4IR::Temp *targetTemp);
+ virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp);
+ virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp);
+ virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName);
+ virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target);
+ virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target);
+ virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target);
+ virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex);
+ virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target);
+ virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName);
+ virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+
+private:
+ 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();
+ };
+
+ Instr::Param getParam(V4IR::Expr *e);
+
+ Instr::Param getResultParam(V4IR::Temp *result)
+ {
+ if (result)
+ return getParam(result);
+ else
+ return Instr::Param::createTemp(scratchTempIndex());
+ }
+
+ void simpleMove(V4IR::Move *);
+ void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &);
+
+ int outgoingArgumentTempStart() const { return _function->tempCount; }
+ int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
+ int frameSize() const { return scratchTempIndex() + 1; }
+
+ template <int Instr>
+ inline ptrdiff_t addInstruction(const InstrData<Instr> &data);
+ ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr);
+ void patchJumpAddresses();
+ uchar *squeezeCode() const;
+
+ QV4::String *identifier(const QString &s);
+
+ V4IR::Function *_function;
+ QV4::Function *_vmFunction;
+ V4IR::BasicBlock *_block;
+ V4IR::BasicBlock *_nextBlock;
+
+ QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
+ QHash<V4IR::BasicBlock *, ptrdiff_t> _addrs;
+
+ uchar *_codeStart;
+ uchar *_codeNext;
+ uchar *_codeEnd;
+
+ StackSlotAllocator *_stackSlotAllocator;
+ V4IR::Stmt *_currentStatement;
+};
+
+class Q_QML_EXPORT ISelFactory: public EvalISelFactory
+{
+public:
+ virtual ~ISelFactory() {}
+ virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module)
+ { return new InstructionSelection(engine, module); }
+ virtual bool jitCompileRegexps() const
+ { return false; }
+};
+
+template<int InstrT>
+ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data)
+{
+ Instr genericInstr;
+ InstrMeta<InstrT>::setData(genericInstr, data);
+ return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr);
+}
+
+} // namespace Moth
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ISEL_MOTH_P_H
diff --git a/src/qml/qml/v4/moth/qv4vme_moth.cpp b/src/qml/qml/v4/moth/qv4vme_moth.cpp
new file mode 100644
index 0000000000..678d0edc4f
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4vme_moth.cpp
@@ -0,0 +1,592 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4vme_moth_p.h"
+#include "qv4instr_moth_p.h"
+#include <private/qv4value_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv4exception_p.h>
+#include <private/qv4math_p.h>
+
+#include <iostream>
+
+#include "qv4alloca_p.h"
+
+#ifdef DO_TRACE_INSTR
+# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I);
+# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); }
+#else
+# define TRACE_INSTR(I)
+# define TRACE(n, str, ...)
+#endif // DO_TRACE_INSTR
+
+using namespace QQmlJS;
+using namespace QQmlJS::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; \
+ if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \
+ context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \
+ Q_UNUSED(instr); \
+ TRACE_INSTR(I)
+
+#ifdef MOTH_THREADED_INTERPRETER
+
+# define MOTH_BEGIN_INSTR(I) op_##I: \
+ MOTH_BEGIN_INSTR_COMMON(I)
+
+# define MOTH_NEXT_INSTR(I) { \
+ genericInstr = reinterpret_cast<const Instr *>(code); \
+ goto *genericInstr->common.code; \
+ }
+
+# define MOTH_END_INSTR(I) } \
+ genericInstr = reinterpret_cast<const Instr *>(code); \
+ goto *genericInstr->common.code; \
+
+#else
+
+# define MOTH_BEGIN_INSTR(I) \
+ case Instr::I: \
+ MOTH_BEGIN_INSTR_COMMON(I)
+
+# define MOTH_NEXT_INSTR(I) { \
+ break; \
+ }
+
+# define MOTH_END_INSTR(I) } \
+ break;
+
+#endif
+
+#ifdef WITH_STATS
+namespace {
+struct VMStats {
+ quint64 paramIsValue;
+ quint64 paramIsArg;
+ quint64 paramIsLocal;
+ quint64 paramIsTemp;
+ quint64 paramIsScopedLocal;
+
+ VMStats()
+ : paramIsValue(0)
+ , paramIsArg(0)
+ , paramIsLocal(0)
+ , paramIsTemp(0)
+ , paramIsScopedLocal(0)
+ {}
+
+ ~VMStats()
+ { show(); }
+
+ void show() {
+ fprintf(stderr, "VM stats:\n");
+ fprintf(stderr, " value: %lu\n", paramIsValue);
+ fprintf(stderr, " arg: %lu\n", paramIsArg);
+ fprintf(stderr, " local: %lu\n", paramIsLocal);
+ fprintf(stderr, " temp: %lu\n", paramIsTemp);
+ fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal);
+ }
+};
+static VMStats vmStats;
+#define VMSTATS(what) ++vmStats.what
+}
+#else // !WITH_STATS
+#define VMSTATS(what) {}
+#endif // WITH_STATS
+
+static inline QV4::Value *getValueRef(QV4::ExecutionContext *context,
+ QV4::Value* stack,
+ const Instr::Param &param
+#if !defined(QT_NO_DEBUG)
+ , unsigned stackSize
+#endif
+ )
+{
+#ifdef DO_TRACE_INSTR
+ if (param.isValue()) {
+ fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData());
+ } else if (param.isArgument()) {
+ fprintf(stderr, " argument %d@%d\n", param.index, param.scope);
+ } else if (param.isLocal()) {
+ fprintf(stderr, " local %d\n", param.index);
+ } else if (param.isTemp()) {
+ fprintf(stderr, " temp %d\n", param.index);
+ } else if (param.isScopedLocal()) {
+ fprintf(stderr, " temp %d@%d\n", param.index, param.scope);
+ } else {
+ Q_ASSERT(!"INVALID");
+ }
+#endif // DO_TRACE_INSTR
+
+ if (param.isValue()) {
+ VMSTATS(paramIsValue);
+ return const_cast<QV4::Value *>(&param.value);
+ } else if (param.isArgument()) {
+ VMSTATS(paramIsArg);
+ QV4::ExecutionContext *c = context;
+ uint scope = param.scope;
+ while (scope--)
+ c = c->outer;
+ QV4::CallContext *cc = static_cast<QV4::CallContext *>(c);
+ const unsigned arg = param.index;
+ Q_ASSERT(arg >= 0);
+ Q_ASSERT((unsigned) arg < cc->argumentCount);
+ Q_ASSERT(cc->arguments);
+ return cc->arguments + arg;
+ } else if (param.isLocal()) {
+ VMSTATS(paramIsLocal);
+ const unsigned index = param.index;
+ QV4::CallContext *c = static_cast<QV4::CallContext *>(context);
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(index < context->variableCount());
+ Q_ASSERT(c->locals);
+ return c->locals + index;
+ } else if (param.isTemp()) {
+ VMSTATS(paramIsTemp);
+ Q_ASSERT(param.index < stackSize);
+ return stack + param.index;
+ } else if (param.isScopedLocal()) {
+ VMSTATS(paramIsScopedLocal);
+ QV4::ExecutionContext *c = context;
+ uint scope = param.scope;
+ while (scope--)
+ c = c->outer;
+ const unsigned index = param.index;
+ QV4::CallContext *cc = static_cast<QV4::CallContext *>(c);
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(index < cc->variableCount());
+ Q_ASSERT(cc->locals);
+ return cc->locals + index;
+ } else {
+ Q_UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+#if defined(QT_NO_DEBUG)
+# define VALUE(param) (*VALUEPTR(param))
+
+// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local.
+# define VALUEPTR(param) \
+ (param.isTemp() ? stack + param.index \
+ : (param.isLocal() ? static_cast<QV4::CallContext *>(context)->locals + param.index \
+ : getValueRef(context, stack, param)))
+#else
+# define VALUE(param) *getValueRef(context, stack, param, stackSize)
+# define VALUEPTR(param) getValueRef(context, stack, param, stackSize)
+#endif
+
+QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
+ QV4::Value *stack, unsigned stackSize
+#ifdef MOTH_THREADED_INTERPRETER
+ , void ***storeJumpTable
+#endif
+ )
+{
+#ifdef DO_TRACE_INSTR
+ qDebug("Starting VME with context=%p and code=%p", context, code);
+#endif // DO_TRACE_INSTR
+
+#ifdef MOTH_THREADED_INTERPRETER
+ if (storeJumpTable) {
+#define MOTH_INSTR_ADDR(I, FMT) &&op_##I,
+ static void *jumpTable[] = {
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR)
+ };
+#undef MOTH_INSTR_ADDR
+ *storeJumpTable = jumpTable;
+ return QV4::Value::undefinedValue();
+ }
+#endif
+
+ context->interpreterInstructionPointer = &code;
+
+#ifdef MOTH_THREADED_INTERPRETER
+ const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
+ goto *genericInstr->common.code;
+#else
+ for (;;) {
+ const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
+ switch (genericInstr->common.instructionType) {
+#endif
+
+ MOTH_BEGIN_INSTR(MoveTemp)
+ VALUE(instr.result) = VALUE(instr.source);
+ MOTH_END_INSTR(MoveTemp)
+
+ MOTH_BEGIN_INSTR(LoadValue)
+// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
+ VALUE(instr.result) = VALUE(instr.value);
+ MOTH_END_INSTR(LoadValue)
+
+ MOTH_BEGIN_INSTR(LoadClosure)
+ __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value);
+ MOTH_END_INSTR(LoadClosure)
+
+ MOTH_BEGIN_INSTR(LoadName)
+ TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData());
+ __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name);
+ MOTH_END_INSTR(LoadName)
+
+ MOTH_BEGIN_INSTR(StoreName)
+ TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData());
+ __qmljs_set_activation_property(context, instr.name, VALUE(instr.source));
+ MOTH_END_INSTR(StoreName)
+
+ MOTH_BEGIN_INSTR(LoadElement)
+ __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index));
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(StoreElement)
+ __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
+ MOTH_END_INSTR(StoreElement)
+
+ MOTH_BEGIN_INSTR(LoadProperty)
+ __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name);
+ MOTH_END_INSTR(LoadProperty)
+
+ MOTH_BEGIN_INSTR(StoreProperty)
+ __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source));
+ MOTH_END_INSTR(StoreProperty)
+
+ MOTH_BEGIN_INSTR(Push)
+ TRACE(inline, "stack size: %u", instr.value);
+ stackSize = instr.value;
+ stack = static_cast<QV4::Value *>(alloca(stackSize * sizeof(QV4::Value)));
+ memset(stack, 0, stackSize * sizeof(QV4::Value));
+ MOTH_END_INSTR(Push)
+
+ MOTH_BEGIN_INSTR(CallValue)
+#ifdef 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;
+ }
+ }
+ }
+#endif // DO_TRACE_INSTR
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc);
+ MOTH_END_INSTR(CallValue)
+
+ MOTH_BEGIN_INSTR(CallProperty)
+ TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc);
+ MOTH_END_INSTR(CallProperty)
+
+ MOTH_BEGIN_INSTR(CallElement)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc);
+ MOTH_END_INSTR(CallElement)
+
+ MOTH_BEGIN_INSTR(CallActivationProperty)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ TRACE(args, "starting at %d, length %d", instr.args, instr.argc);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc);
+ MOTH_END_INSTR(CallActivationProperty)
+
+ MOTH_BEGIN_INSTR(CallBuiltinThrow)
+ __qmljs_throw(context, VALUE(instr.arg));
+ MOTH_END_INSTR(CallBuiltinThrow)
+
+ MOTH_BEGIN_INSTR(EnterTry)
+ VALUE(instr.exceptionVar) = QV4::Value::undefinedValue();
+ try {
+ const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset;
+ run(context, tryCode, stack, stackSize);
+ code = tryCode;
+ context->interpreterInstructionPointer = &code;
+ } catch (QV4::Exception &ex) {
+ ex.accept(context);
+ VALUE(instr.exceptionVar) = ex.value();
+ try {
+ QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context);
+ const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset;
+ run(catchContext, catchCode, stack, stackSize);
+ code = catchCode;
+ context->interpreterInstructionPointer = &code;
+ context = __qmljs_builtin_pop_scope(catchContext);
+ } catch (QV4::Exception &ex) {
+ ex.accept(context);
+ VALUE(instr.exceptionVar) = ex.value();
+ const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset;
+ run(context, catchCode, stack, stackSize);
+ code = catchCode;
+ context->interpreterInstructionPointer = &code;
+ }
+ }
+ MOTH_END_INSTR(EnterTry)
+
+ MOTH_BEGIN_INSTR(CallBuiltinFinishTry)
+ return QV4::Value();
+ MOTH_END_INSTR(CallBuiltinFinishTry)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPushScope)
+ context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context);
+ MOTH_END_INSTR(CallBuiltinPushScope)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPopScope)
+ context = __qmljs_builtin_pop_scope(context);
+ MOTH_END_INSTR(CallBuiltinPopScope)
+
+ MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject)
+ __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg));
+ MOTH_END_INSTR(CallBuiltinForeachIteratorObject)
+
+ MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName)
+ __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg));
+ MOTH_END_INSTR(CallBuiltinForeachNextPropertyName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteMember)
+ __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member);
+ MOTH_END_INSTR(CallBuiltinDeleteMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript)
+ __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index));
+ MOTH_END_INSTR(CallBuiltinDeleteSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteName)
+ __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name);
+ MOTH_END_INSTR(CallBuiltinDeleteName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofMember)
+ __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript)
+ __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index));
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofName)
+ __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofValue)
+ __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value));
+ MOTH_END_INSTR(CallBuiltinTypeofValue)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncMember)
+ __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript)
+ __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index));
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncName)
+ __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncValue)
+ __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value));
+ MOTH_END_INSTR(CallBuiltinTypeofValue)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecMember)
+ __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript)
+ __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index));
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecName)
+ __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecValue)
+ __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value));
+ MOTH_END_INSTR(CallBuiltinTypeofValue)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeclareVar)
+ __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName);
+ MOTH_END_INSTR(CallBuiltinDeclareVar)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter)
+ __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter));
+ MOTH_END_INSTR(CallBuiltinDefineGetterSetter)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineProperty)
+ __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value));
+ MOTH_END_INSTR(CallBuiltinDefineProperty)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineArray)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc);
+ MOTH_END_INSTR(CallBuiltinDefineArray)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral)
+ QV4::Value *args = stack + instr.args;
+ __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass);
+ MOTH_END_INSTR(CallBuiltinDefineObjectLiteral)
+
+ MOTH_BEGIN_INSTR(CreateValue)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc);
+ MOTH_END_INSTR(CreateValue)
+
+ MOTH_BEGIN_INSTR(CreateProperty)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc);
+ MOTH_END_INSTR(CreateProperty)
+
+ MOTH_BEGIN_INSTR(CreateActivationProperty)
+ TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc);
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ QV4::Value *args = stack + instr.args;
+ __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc);
+ MOTH_END_INSTR(CreateActivationProperty)
+
+ MOTH_BEGIN_INSTR(Jump)
+ code = ((uchar *)&instr.offset) + instr.offset;
+ MOTH_END_INSTR(Jump)
+
+ MOTH_BEGIN_INSTR(CJump)
+ uint cond = __qmljs_to_boolean(VALUE(instr.condition));
+ TRACE(condition, "%s", cond ? "TRUE" : "FALSE");
+ if (cond)
+ code = ((uchar *)&instr.offset) + instr.offset;
+ MOTH_END_INSTR(CJump)
+
+ MOTH_BEGIN_INSTR(Unop)
+ instr.alu(VALUEPTR(instr.result), VALUE(instr.source));
+ MOTH_END_INSTR(Unop)
+
+ MOTH_BEGIN_INSTR(Binop)
+ instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs));
+ MOTH_END_INSTR(Binop)
+
+ MOTH_BEGIN_INSTR(AddNumberParams)
+ QV4::Value lhs = VALUE(instr.lhs);
+ QV4::Value rhs = VALUE(instr.rhs);
+ if (lhs.isInteger() && rhs.isInteger())
+ VALUE(instr.result) = QV4::add_int32(lhs.integerValue(), rhs.integerValue());
+ else
+ VALUEPTR(instr.result)->setDouble(lhs.asDouble() + rhs.asDouble());
+ MOTH_END_INSTR(AddNumberParams)
+
+ MOTH_BEGIN_INSTR(MulNumberParams)
+ QV4::Value lhs = VALUE(instr.lhs);
+ QV4::Value rhs = VALUE(instr.rhs);
+ if (lhs.isInteger() && rhs.isInteger())
+ VALUE(instr.result) = QV4::mul_int32(lhs.integerValue(), rhs.integerValue());
+ else
+ VALUEPTR(instr.result)->setDouble(lhs.asDouble() * rhs.asDouble());
+ MOTH_END_INSTR(MulNumberParams)
+
+ MOTH_BEGIN_INSTR(SubNumberParams)
+ QV4::Value lhs = VALUE(instr.lhs);
+ QV4::Value rhs = VALUE(instr.rhs);
+ if (lhs.isInteger() && rhs.isInteger())
+ VALUE(instr.result) = QV4::sub_int32(lhs.integerValue(), rhs.integerValue());
+ else
+ VALUEPTR(instr.result)->setDouble(lhs.asDouble() - rhs.asDouble());
+ MOTH_END_INSTR(SubNumberParams)
+
+ MOTH_BEGIN_INSTR(Ret)
+ QV4::Value &result = VALUE(instr.result);
+// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData());
+ return result;
+ MOTH_END_INSTR(Ret)
+
+ MOTH_BEGIN_INSTR(LoadThis)
+ VALUE(instr.result) = context->thisObject;
+ MOTH_END_INSTR(LoadThis)
+
+ MOTH_BEGIN_INSTR(InplaceElementOp)
+ instr.alu(context,
+ VALUE(instr.base),
+ VALUE(instr.index),
+ VALUE(instr.source));
+ MOTH_END_INSTR(InplaceElementOp)
+
+ MOTH_BEGIN_INSTR(InplaceMemberOp)
+ instr.alu(context,
+ VALUE(instr.base),
+ instr.member,
+ VALUE(instr.source));
+ MOTH_END_INSTR(InplaceMemberOp)
+
+ MOTH_BEGIN_INSTR(InplaceNameOp)
+ TRACE(name, "%s", instr.name->toQString().toUtf8().constData());
+ instr.alu(context, instr.name, VALUE(instr.source));
+ MOTH_END_INSTR(InplaceNameOp)
+
+#ifdef MOTH_THREADED_INTERPRETER
+ // nothing to do
+#else
+ default:
+ qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType);
+ break;
+ }
+ }
+#endif
+
+}
+
+#ifdef MOTH_THREADED_INTERPRETER
+void **VME::instructionJumpTable()
+{
+ static void **jumpTable = 0;
+ if (!jumpTable) {
+ const uchar *code = 0;
+ VME().run(0, code, 0, 0, &jumpTable);
+ }
+ return jumpTable;
+}
+#endif
+
+QV4::Value VME::exec(QV4::ExecutionContext *ctxt, const uchar *code)
+{
+ VME vme;
+ return vme.run(ctxt, code);
+}
diff --git a/src/qml/qml/v4/moth/qv4vme_moth_p.h b/src/qml/qml/v4/moth/qv4vme_moth_p.h
new file mode 100644
index 0000000000..59692500ba
--- /dev/null
+++ b/src/qml/qml/v4/moth/qv4vme_moth_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4VME_MOTH_P_H
+#define QV4VME_MOTH_P_H
+
+#include <private/qv4runtime_p.h>
+#include "qv4instr_moth_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Moth {
+
+class VME
+{
+public:
+ static QV4::Value exec(QV4::ExecutionContext *, const uchar *);
+
+#ifdef MOTH_THREADED_INTERPRETER
+ static void **instructionJumpTable();
+#endif
+
+private:
+ QV4::Value run(QV4::ExecutionContext *, const uchar *&code,
+ QV4::Value *stack = 0, unsigned stackSize = 0
+#ifdef MOTH_THREADED_INTERPRETER
+ , void ***storeJumpTable = 0
+#endif
+ );
+};
+
+} // namespace Moth
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4VME_MOTH_P_H
diff --git a/src/qml/qml/v4/qv4_llvm_p.h b/src/qml/qml/v4/qv4_llvm_p.h
new file mode 100644
index 0000000000..50bd7d3831
--- /dev/null
+++ b/src/qml/qml/v4/qv4_llvm_p.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4_LLVM_P_H
+#define QV4_LLVM_P_H
+
+#include "qv4global_p.h"
+#include "qv4jsir_p.h"
+
+#include <QtCore/QString>
+
+namespace QQmlJS {
+
+// Note: keep this enum in sync with the command-line option!
+enum LLVMOutputType {
+ LLVMOutputJit = -1,
+ LLVMOutputIR = 0, // .ll
+ LLVMOutputBitcode = 1, // .bc
+ LLVMOutputAssembler = 2, // .s
+ LLVMOutputObject = 3 // .o
+};
+
+Q_QML_EXPORT int compileWithLLVM(V4IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*)(void *));
+
+} // QQmlJS
+
+#endif // QV4_LLVM_P_H
diff --git a/src/qml/qml/v4/qv4alloca_p.h b/src/qml/qml/v4/qv4alloca_p.h
new file mode 100644
index 0000000000..e4580da3d8
--- /dev/null
+++ b/src/qml/qml/v4/qv4alloca_p.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4_ALLOCA_H
+#define QV4_ALLOCA_H
+
+#include <qglobal.h>
+
+#if defined(Q_OS_WIN)
+#include <malloc.h>
+#define alloca _alloca
+#else
+#include <alloca.h>
+#endif
+
+#endif
diff --git a/src/qml/qml/v4/qv4argumentsobject.cpp b/src/qml/qml/v4/qv4argumentsobject.cpp
new file mode 100644
index 0000000000..6247ef1504
--- /dev/null
+++ b/src/qml/qml/v4/qv4argumentsobject.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4argumentsobject_p.h>
+
+using namespace QV4;
+
+static Value throwTypeError(SimpleCallContext *ctx)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+DEFINE_MANAGED_VTABLE(ArgumentsObject);
+
+ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount)
+ : Object(context->engine), context(context)
+{
+ vtbl = &static_vtbl;
+ type = Type_ArgumentsObject;
+
+ defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount));
+ if (context->strictMode) {
+ for (uint i = 0; i < context->argumentCount; ++i)
+ Object::put(context, QString::number(i), context->arguments[i]);
+ FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError);
+ Property pd = Property::fromAccessor(thrower, thrower);
+ __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ } else {
+ uint numAccessors = qMin(formalParameterCount, actualParameterCount);
+ context->engine->requireArgumentsAccessors(numAccessors);
+ for (uint i = 0; i < (uint)numAccessors; ++i) {
+ mappedArguments.append(context->argument(i));
+ __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor);
+ }
+ for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) {
+ Property pd = Property::fromValue(context->argument(i));
+ __defineOwnProperty__(context, i, pd, Attr_Data);
+ }
+ defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function));
+ isNonStrictArgumentsObject = true;
+ }
+}
+
+void ArgumentsObject::destroy(Managed *that)
+{
+ static_cast<ArgumentsObject *>(that)->~ArgumentsObject();
+}
+
+bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs)
+{
+ uint pidx = propertyIndexFromArrayIndex(index);
+ Property *pd = arrayData + pidx;
+ Property map;
+ PropertyAttributes mapAttrs;
+ bool isMapped = false;
+ if (pd && index < (uint)mappedArguments.size())
+ isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter();
+
+ if (isMapped) {
+ map = *pd;
+ mapAttrs = arrayAttributes[pidx];
+ arrayAttributes[pidx] = Attr_Data;
+ pd->value = mappedArguments.at(index);
+ }
+
+ isNonStrictArgumentsObject = false;
+ bool strict = ctx->strictMode;
+ ctx->strictMode = false;
+ bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs);
+ ctx->strictMode = strict;
+ isNonStrictArgumentsObject = true;
+
+ if (isMapped && attrs.isData()) {
+ if (!attrs.isGeneric()) {
+ Value arg = desc.value;
+ map.setter()->call(Value::fromObject(this), &arg, 1);
+ }
+ if (attrs.isWritable()) {
+ *pd = map;
+ arrayAttributes[pidx] = mapAttrs;
+ }
+ }
+
+ if (ctx->strictMode && !result)
+ ctx->throwTypeError();
+ return result;
+}
+
+DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction);
+
+Value ArgumentsGetterFunction::call(Managed *getter, const Value &thisObject, Value *, int)
+{
+ ArgumentsGetterFunction *g = static_cast<ArgumentsGetterFunction *>(getter);
+ Object *that = thisObject.asObject();
+ if (!that)
+ getter->engine()->current->throwTypeError();
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ getter->engine()->current->throwTypeError();
+
+ assert(g->index < o->context->argumentCount);
+ return o->context->argument(g->index);
+}
+
+DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction);
+
+Value ArgumentsSetterFunction::call(Managed *setter, const Value &thisObject, Value *args, int argc)
+{
+ ArgumentsSetterFunction *s = static_cast<ArgumentsSetterFunction *>(setter);
+ Object *that = thisObject.asObject();
+ if (!that)
+ setter->engine()->current->throwTypeError();
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ setter->engine()->current->throwTypeError();
+
+ assert(s->index < o->context->argumentCount);
+ o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue();
+ return Value::undefinedValue();
+}
+
+void ArgumentsObject::markObjects(Managed *that)
+{
+ ArgumentsObject *o = static_cast<ArgumentsObject *>(that);
+ o->context->mark();
+ for (int i = 0; i < o->mappedArguments.size(); ++i) {
+ Managed *m = o->mappedArguments.at(i).asManaged();
+ if (m)
+ m->mark();
+ }
+ Object::markObjects(that);
+}
diff --git a/src/qml/qml/v4/qv4argumentsobject_p.h b/src/qml/qml/v4/qv4argumentsobject_p.h
new file mode 100644
index 0000000000..3d760b8937
--- /dev/null
+++ b/src/qml/qml/v4/qv4argumentsobject_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ARGUMENTSOBJECTS_H
+#define QV4ARGUMENTSOBJECTS_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ArgumentsGetterFunction: FunctionObject
+{
+ uint index;
+
+ ArgumentsGetterFunction(ExecutionContext *scope, uint index)
+ : FunctionObject(scope), index(index) { vtbl = &static_vtbl; }
+
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ArgumentsSetterFunction: FunctionObject
+{
+ uint index;
+
+ ArgumentsSetterFunction(ExecutionContext *scope, uint index)
+ : FunctionObject(scope), index(index) { vtbl = &static_vtbl; }
+
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+
+struct ArgumentsObject: Object {
+ CallContext *context;
+ QVector<Value> mappedArguments;
+ ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount);
+ ~ArgumentsObject() {}
+
+ bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs);
+
+ static void markObjects(Managed *that);
+protected:
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/qml/qml/v4/qv4arrayobject.cpp b/src/qml/qml/v4/qv4arrayobject.cpp
new file mode 100644
index 0000000000..b1f25177dd
--- /dev/null
+++ b/src/qml/qml/v4/qv4arrayobject.cpp
@@ -0,0 +1,865 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4arrayobject_p.h"
+#include "qv4sparsearray_p.h"
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(ArrayCtor);
+
+ArrayCtor::ArrayCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Array")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value ArrayCtor::construct(Managed *m, Value *argv, int argc)
+{
+ ExecutionEngine *v4 = m->engine();
+ ArrayObject *a = v4->newArrayObject();
+ uint len;
+ if (argc == 1 && argv[0].isNumber()) {
+ bool ok;
+ len = argv[0].asArrayLength(&ok);
+
+ if (!ok)
+ v4->current->throwRangeError(argv[0]);
+
+ if (len < 0x1000)
+ a->arrayReserve(len);
+ } else {
+ len = argc;
+ a->arrayReserve(len);
+ for (unsigned int i = 0; i < len; ++i)
+ a->arrayData[i].value = argv[i];
+ a->arrayDataLen = len;
+ }
+ a->setArrayLengthUnchecked(len);
+
+ return Value::fromObject(a);
+}
+
+Value ArrayCtor::call(Managed *that, const Value &, Value *argv, int argc)
+{
+ return construct(that, argv, argc);
+}
+
+ArrayPrototype::ArrayPrototype(ExecutionContext *context)
+ : ArrayObject(context->engine)
+{
+}
+
+void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1);
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1);
+ defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0);
+ defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1);
+ defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0);
+ defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0);
+ defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1);
+ defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2);
+ defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1);
+ defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
+ defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1);
+ defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1);
+ defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1);
+ defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1);
+ defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1);
+ defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1);
+}
+
+uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o)
+{
+ if (o->isArrayObject())
+ return o->arrayLength();
+ return o->get(ctx->engine->id_length).toUInt32();
+}
+
+Value ArrayPrototype::method_isArray(SimpleCallContext *ctx)
+{
+ Value arg = ctx->argument(0);
+ bool isArray = arg.asArrayObject();
+ return Value::fromBoolean(isArray);
+}
+
+Value ArrayPrototype::method_toString(SimpleCallContext *ctx)
+{
+ return method_join(ctx);
+}
+
+Value ArrayPrototype::method_toLocaleString(SimpleCallContext *ctx)
+{
+ return method_toString(ctx);
+}
+
+Value ArrayPrototype::method_concat(SimpleCallContext *ctx)
+{
+ ArrayObject *result = ctx->engine->newArrayObject();
+
+ if (ArrayObject *instance = ctx->thisObject.asArrayObject()) {
+ result->copyArrayData(instance);
+ } else {
+ QString v = ctx->thisObject.toString(ctx)->toQString();
+ result->arraySet(0, Value::fromString(ctx, v));
+ }
+
+ for (uint i = 0; i < ctx->argumentCount; ++i) {
+ Value arg = ctx->argument(i);
+
+ if (ArrayObject *elt = arg.asArrayObject())
+ result->arrayConcat(elt);
+
+ else
+ result->arraySet(getLength(ctx, result), arg);
+ }
+
+ return Value::fromObject(result);
+}
+
+Value ArrayPrototype::method_join(SimpleCallContext *ctx)
+{
+ Value arg = ctx->argument(0);
+
+ QString r4;
+ if (arg.isUndefined())
+ r4 = QStringLiteral(",");
+ else
+ r4 = arg.toString(ctx)->toQString();
+
+ Value self = ctx->thisObject;
+ const Value length = self.property(ctx, ctx->engine->id_length);
+ const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber());
+
+ static QSet<Object *> visitedArrayElements;
+
+ if (! r2 || visitedArrayElements.contains(self.objectValue()))
+ return Value::fromString(ctx, QString());
+
+ // avoid infinite recursion
+ visitedArrayElements.insert(self.objectValue());
+
+ QString R;
+
+ // ### FIXME
+ if (ArrayObject *a = self.asArrayObject()) {
+ for (uint i = 0; i < a->arrayLength(); ++i) {
+ if (i)
+ R += r4;
+
+ Value e = a->getIndexed(i);
+ if (! (e.isUndefined() || e.isNull()))
+ R += e.toString(ctx)->toQString();
+ }
+ } else {
+ //
+ // crazy!
+ //
+ Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0")));
+ if (!(r6.isUndefined() || r6.isNull()))
+ R = r6.toString(ctx)->toQString();
+
+ for (quint32 k = 1; k < r2; ++k) {
+ R += r4;
+
+ String *name = Value::fromDouble(k).toString(ctx);
+ Value r12 = self.property(ctx, name);
+
+ if (! (r12.isUndefined() || r12.isNull()))
+ R += r12.toString(ctx)->toQString();
+ }
+ }
+
+ visitedArrayElements.remove(self.objectValue());
+ return Value::fromString(ctx, R);
+}
+
+Value ArrayPrototype::method_pop(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+
+ if (!len) {
+ if (!instance->isArrayObject())
+ instance->put(ctx->engine->id_length, Value::fromInt32(0));
+ return Value::undefinedValue();
+ }
+
+ Value result = instance->getIndexed(len - 1);
+
+ instance->deleteIndexedProperty(len - 1);
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len - 1);
+ else
+ instance->put(ctx->engine->id_length, Value::fromDouble(len - 1));
+ return result;
+}
+
+Value ArrayPrototype::method_push(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+
+ if (len + ctx->argumentCount < len) {
+ // ughh...
+ double l = len;
+ for (double i = 0; i < ctx->argumentCount; ++i) {
+ Value idx = Value::fromDouble(l + i);
+ instance->put(idx.toString(ctx), ctx->argument(i));
+ }
+ double newLen = l + ctx->argumentCount;
+ if (!instance->isArrayObject())
+ instance->put(ctx->engine->id_length, Value::fromDouble(newLen));
+ else
+ ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow")));
+ return Value::fromDouble(newLen);
+ }
+
+ bool protoHasArray = false;
+ Object *p = instance;
+ while ((p = p->prototype))
+ if (p->arrayDataLen)
+ protoHasArray = true;
+
+ if (!protoHasArray && instance->arrayDataLen <= len) {
+ for (uint i = 0; i < ctx->argumentCount; ++i) {
+ Value v = ctx->argument(i);
+
+ if (!instance->sparseArray) {
+ if (len >= instance->arrayAlloc)
+ instance->arrayReserve(len + 1);
+ instance->arrayData[len].value = v;
+ if (instance->arrayAttributes)
+ instance->arrayAttributes[len] = Attr_Data;
+ instance->arrayDataLen = len + 1;
+ } else {
+ uint i = instance->allocArrayValue(v);
+ instance->sparseArray->push_back(i, len);
+ }
+ ++len;
+ }
+ } else {
+ for (uint i = 0; i < ctx->argumentCount; ++i)
+ instance->putIndexed(len + i, ctx->argument(i));
+ len += ctx->argumentCount;
+ }
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len);
+ else
+ instance->put(ctx->engine->id_length, Value::fromDouble(len));
+
+ if (len < INT_MAX)
+ return Value::fromInt32(len);
+ return Value::fromDouble((double)len);
+
+}
+
+Value ArrayPrototype::method_reverse(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint length = getLength(ctx, instance);
+
+ int lo = 0, hi = length - 1;
+
+ for (; lo < hi; ++lo, --hi) {
+ bool loExists, hiExists;
+ Value lval = instance->getIndexed(lo, &loExists);
+ Value hval = instance->getIndexed(hi, &hiExists);
+ if (hiExists)
+ instance->putIndexed(lo, hval);
+ else
+ instance->deleteIndexedProperty(lo);
+ if (loExists)
+ instance->putIndexed(hi, lval);
+ else
+ instance->deleteIndexedProperty(hi);
+ }
+ return Value::fromObject(instance);
+}
+
+Value ArrayPrototype::method_shift(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+
+ if (!len) {
+ if (!instance->isArrayObject())
+ instance->put(ctx->engine->id_length, Value::fromInt32(0));
+ return Value::undefinedValue();
+ }
+
+ Property *front = 0;
+ uint pidx = instance->propertyIndexFromArrayIndex(0);
+ if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric()))
+ front = instance->arrayData + pidx;
+
+ Value result = front ? instance->getValue(front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue();
+
+ bool protoHasArray = false;
+ Object *p = instance;
+ while ((p = p->prototype))
+ if (p->arrayDataLen)
+ protoHasArray = true;
+
+ if (!protoHasArray && instance->arrayDataLen <= len) {
+ if (!instance->sparseArray) {
+ if (instance->arrayDataLen) {
+ ++instance->arrayOffset;
+ ++instance->arrayData;
+ --instance->arrayDataLen;
+ --instance->arrayAlloc;
+ if (instance->arrayAttributes)
+ ++instance->arrayAttributes;
+ }
+ } else {
+ uint idx = instance->sparseArray->pop_front();
+ instance->freeArrayValue(idx);
+ }
+ } else {
+ // do it the slow way
+ for (uint k = 1; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (exists)
+ instance->putIndexed(k - 1, v);
+ else
+ instance->deleteIndexedProperty(k - 1);
+ }
+ instance->deleteIndexedProperty(len - 1);
+ }
+
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len - 1);
+ else
+ instance->put(ctx->engine->id_length, Value::fromDouble(len - 1));
+ return result;
+}
+
+Value ArrayPrototype::method_slice(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.toObject(ctx);
+
+ ArrayObject *result = ctx->engine->newArrayObject();
+ uint len = o->get(ctx->engine->id_length).toUInt32();
+ double s = ctx->argument(0).toInteger();
+ uint start;
+ if (s < 0)
+ start = (uint)qMax(len + s, 0.);
+ else if (s > len)
+ start = len;
+ else
+ start = (uint) s;
+ uint end = len;
+ if (!ctx->argument(1).isUndefined()) {
+ double e = ctx->argument(1).toInteger();
+ if (e < 0)
+ end = (uint)qMax(len + e, 0.);
+ else if (e > len)
+ end = len;
+ else
+ end = (uint) e;
+ }
+
+ uint n = 0;
+ for (uint i = start; i < end; ++i) {
+ bool exists;
+ Value v = o->getIndexed(i, &exists);
+ if (exists) {
+ result->arraySet(n, v);
+ }
+ ++n;
+ }
+ return Value::fromObject(result);
+}
+
+Value ArrayPrototype::method_sort(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ Value comparefn = ctx->argument(0);
+ instance->arraySort(ctx, instance, comparefn, len);
+ return ctx->thisObject;
+}
+
+Value ArrayPrototype::method_splice(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+
+ ArrayObject *newArray = ctx->engine->newArrayObject();
+
+ double rs = ctx->argument(0).toInteger();
+ uint start;
+ if (rs < 0)
+ start = (uint) qMax(0., len + rs);
+ else
+ start = (uint) qMin(rs, (double)len);
+
+ uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(), 0.), (double)(len - start));
+
+ newArray->arrayReserve(deleteCount);
+ Property *pd = newArray->arrayData;
+ for (uint i = 0; i < deleteCount; ++i) {
+ pd->value = instance->getIndexed(start + i);
+ ++pd;
+ }
+ newArray->arrayDataLen = deleteCount;
+ newArray->setArrayLengthUnchecked(deleteCount);
+
+ uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2;
+
+ if (itemCount < deleteCount) {
+ for (uint k = start; k < len - deleteCount; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k + deleteCount, &exists);
+ if (exists)
+ instance->putIndexed(k + itemCount, v);
+ else
+ instance->deleteIndexedProperty(k + itemCount);
+ }
+ for (uint k = len; k > len - deleteCount + itemCount; --k)
+ instance->deleteIndexedProperty(k - 1);
+ } else if (itemCount > deleteCount) {
+ uint k = len - deleteCount;
+ while (k > start) {
+ bool exists;
+ Value v = instance->getIndexed(k + deleteCount - 1, &exists);
+ if (exists)
+ instance->putIndexed(k + itemCount - 1, v);
+ else
+ instance->deleteIndexedProperty(k + itemCount - 1);
+ --k;
+ }
+ }
+
+ for (uint i = 0; i < itemCount; ++i)
+ instance->putIndexed(start + i, ctx->argument(i + 2));
+
+ ctx->strictMode = true;
+ instance->put(ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount));
+
+ return Value::fromObject(newArray);
+}
+
+Value ArrayPrototype::method_unshift(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+
+ bool protoHasArray = false;
+ Object *p = instance;
+ while ((p = p->prototype))
+ if (p->arrayDataLen)
+ protoHasArray = true;
+
+ if (!protoHasArray && instance->arrayDataLen <= len) {
+ for (int i = ctx->argumentCount - 1; i >= 0; --i) {
+ Value v = ctx->argument(i);
+
+ if (!instance->sparseArray) {
+ if (!instance->arrayOffset)
+ instance->getArrayHeadRoom();
+
+ --instance->arrayOffset;
+ --instance->arrayData;
+ ++instance->arrayDataLen;
+ if (instance->arrayAttributes) {
+ --instance->arrayAttributes;
+ *instance->arrayAttributes = Attr_Data;
+ }
+ instance->arrayData->value = v;
+ } else {
+ uint idx = instance->allocArrayValue(v);
+ instance->sparseArray->push_front(idx);
+ }
+ }
+ } else {
+ for (uint k = len; k > 0; --k) {
+ bool exists;
+ Value v = instance->getIndexed(k - 1, &exists);
+ if (exists)
+ instance->putIndexed(k + ctx->argumentCount - 1, v);
+ else
+ instance->deleteIndexedProperty(k + ctx->argumentCount - 1);
+ }
+ for (uint i = 0; i < ctx->argumentCount; ++i)
+ instance->putIndexed(i, ctx->argument(i));
+ }
+
+ uint newLen = len + ctx->argumentCount;
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(newLen);
+ else
+ instance->put(ctx->engine->id_length, Value::fromDouble(newLen));
+
+ if (newLen < INT_MAX)
+ return Value::fromInt32(newLen);
+ return Value::fromDouble((double)newLen);
+}
+
+Value ArrayPrototype::method_indexOf(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+ if (!len)
+ return Value::fromInt32(-1);
+
+ Value searchValue;
+ uint fromIndex = 0;
+
+ if (ctx->argumentCount >= 1)
+ searchValue = ctx->argument(0);
+ else
+ searchValue = Value::undefinedValue();
+
+ if (ctx->argumentCount >= 2) {
+ double f = ctx->argument(1).toInteger();
+ if (f >= len)
+ return Value::fromInt32(-1);
+ if (f < 0)
+ f = qMax(len + f, 0.);
+ fromIndex = (uint) f;
+ }
+
+ if (instance->isStringObject()) {
+ for (uint k = fromIndex; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (exists && __qmljs_strict_equal(v, searchValue))
+ return Value::fromDouble(k);
+ }
+ return Value::fromInt32(-1);
+ }
+
+ return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance);
+}
+
+Value ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+ uint len = getLength(ctx, instance);
+ if (!len)
+ return Value::fromInt32(-1);
+
+ Value searchValue;
+ uint fromIndex = len;
+
+ if (ctx->argumentCount >= 1)
+ searchValue = ctx->argument(0);
+ else
+ searchValue = Value::undefinedValue();
+
+ if (ctx->argumentCount >= 2) {
+ double f = ctx->argument(1).toInteger();
+ if (f > 0)
+ f = qMin(f, (double)(len - 1));
+ else if (f < 0) {
+ f = len + f;
+ if (f < 0)
+ return Value::fromInt32(-1);
+ }
+ fromIndex = (uint) f + 1;
+ }
+
+ for (uint k = fromIndex; k > 0;) {
+ --k;
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (exists && __qmljs_strict_equal(v, searchValue))
+ return Value::fromDouble(k);
+ }
+ return Value::fromInt32(-1);
+}
+
+Value ArrayPrototype::method_every(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ Value thisArg = ctx->argument(1);
+
+ bool ok = true;
+ for (uint k = 0; ok && k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value r = callback->call(thisArg, args, 3);
+ ok = r.toBoolean();
+ }
+ return Value::fromBoolean(ok);
+}
+
+Value ArrayPrototype::method_some(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ Value thisArg = ctx->argument(1);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value r = callback->call(thisArg, args, 3);
+ if (r.toBoolean())
+ return Value::fromBoolean(true);
+ }
+ return Value::fromBoolean(false);
+}
+
+Value ArrayPrototype::method_forEach(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ Value thisArg = ctx->argument(1);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ callback->call(thisArg, args, 3);
+ }
+ return Value::undefinedValue();
+}
+
+Value ArrayPrototype::method_map(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ Value thisArg = ctx->argument(1);
+
+ ArrayObject *a = ctx->engine->newArrayObject();
+ a->arrayReserve(len);
+ a->setArrayLengthUnchecked(len);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value mapped = callback->call(thisArg, args, 3);
+ a->arraySet(k, mapped);
+ }
+ return Value::fromObject(a);
+}
+
+Value ArrayPrototype::method_filter(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ Value thisArg = ctx->argument(1);
+
+ ArrayObject *a = ctx->engine->newArrayObject();
+ a->arrayReserve(len);
+
+ uint to = 0;
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value selected = callback->call(thisArg, args, 3);
+ if (selected.toBoolean()) {
+ a->arraySet(to, v);
+ ++to;
+ }
+ }
+ return Value::fromObject(a);
+}
+
+Value ArrayPrototype::method_reduce(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ uint k = 0;
+ Value acc;
+ if (ctx->argumentCount > 1) {
+ acc = ctx->argument(1);
+ } else {
+ bool kPresent = false;
+ while (k < len && !kPresent) {
+ Value v = instance->getIndexed(k, &kPresent);
+ if (kPresent)
+ acc = v;
+ ++k;
+ }
+ if (!kPresent)
+ ctx->throwTypeError();
+ }
+
+ while (k < len) {
+ bool kPresent;
+ Value v = instance->getIndexed(k, &kPresent);
+ if (kPresent) {
+ Value args[4];
+ args[0] = acc;
+ args[1] = v;
+ args[2] = Value::fromDouble(k);
+ args[3] = ctx->thisObject;
+ acc = callback->call(Value::undefinedValue(), args, 4);
+ }
+ ++k;
+ }
+ return acc;
+}
+
+Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx)
+{
+ Object *instance = ctx->thisObject.toObject(ctx);
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ ctx->throwTypeError();
+
+ if (len == 0) {
+ if (ctx->argumentCount == 1)
+ ctx->throwTypeError();
+ return ctx->argument(1);
+ }
+
+ uint k = len;
+ Value acc;
+ if (ctx->argumentCount > 1) {
+ acc = ctx->argument(1);
+ } else {
+ bool kPresent = false;
+ while (k > 0 && !kPresent) {
+ Value v = instance->getIndexed(k - 1, &kPresent);
+ if (kPresent)
+ acc = v;
+ --k;
+ }
+ if (!kPresent)
+ ctx->throwTypeError();
+ }
+
+ while (k > 0) {
+ bool kPresent;
+ Value v = instance->getIndexed(k - 1, &kPresent);
+ if (kPresent) {
+ Value args[4];
+ args[0] = acc;
+ args[1] = v;
+ args[2] = Value::fromDouble(k - 1);
+ args[3] = ctx->thisObject;
+ acc = callback->call(Value::undefinedValue(), args, 4);
+ }
+ --k;
+ }
+ return acc;
+}
+
diff --git a/src/qml/qml/v4/qv4arrayobject_p.h b/src/qml/qml/v4/qv4arrayobject_p.h
new file mode 100644
index 0000000000..13c3882f4f
--- /dev/null
+++ b/src/qml/qml/v4/qv4arrayobject_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ARRAYOBJECT_H
+#define QV4ARRAYOBJECT_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ArrayCtor: FunctionObject
+{
+ ArrayCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ArrayPrototype: ArrayObject
+{
+ ArrayPrototype(ExecutionContext *context);
+
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static uint getLength(ExecutionContext *ctx, Object *o);
+
+ static Value method_isArray(SimpleCallContext *ctx);
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_toLocaleString(SimpleCallContext *ctx);
+ static Value method_concat(SimpleCallContext *ctx);
+ static Value method_join(SimpleCallContext *ctx);
+ static Value method_pop(SimpleCallContext *ctx);
+ static Value method_push(SimpleCallContext *ctx);
+ static Value method_reverse(SimpleCallContext *ctx);
+ static Value method_shift(SimpleCallContext *ctx);
+ static Value method_slice(SimpleCallContext *ctx);
+ static Value method_sort(SimpleCallContext *ctx);
+ static Value method_splice(SimpleCallContext *ctx);
+ static Value method_unshift(SimpleCallContext *ctx);
+ static Value method_indexOf(SimpleCallContext *ctx);
+ static Value method_lastIndexOf(SimpleCallContext *ctx);
+ static Value method_every(SimpleCallContext *ctx);
+ static Value method_some(SimpleCallContext *ctx);
+ static Value method_forEach(SimpleCallContext *ctx);
+ static Value method_map(SimpleCallContext *ctx);
+ static Value method_filter(SimpleCallContext *ctx);
+ static Value method_reduce(SimpleCallContext *ctx);
+ static Value method_reduceRight(SimpleCallContext *ctx);
+};
+
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp
deleted file mode 100644
index 668f7b4d05..0000000000
--- a/src/qml/qml/v4/qv4bindings.cpp
+++ /dev/null
@@ -1,2478 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// #define REGISTER_CLEANUP_DEBUG
-
-#include "qv4bindings_p.h"
-#include "qv4program_p.h"
-#include "qv4compiler_p.h"
-#include "qv4compiler_p_p.h"
-
-#include <private/qqmlglobal_p.h>
-
-#include <private/qv8_p.h>
-#include <private/qjsconverter_p.h>
-#include <private/qjsconverter_impl_p.h>
-#include <private/qjsvalue_impl_p.h>
-#include <private/qjsvalueiterator_impl_p.h>
-#include <private/qv8engine_impl_p.h>
-
-#include <private/qqmlaccessors_p.h>
-#include <private/qqmlprofilerservice_p.h>
-#include <private/qqmlmetatype_p.h>
-#include <private/qqmltrace_p.h>
-#include <private/qqmlstringconverters_p.h>
-#include <private/qqmlproperty_p.h>
-#include <private/qqmlvmemetaobject_p.h>
-
-#include <QtQml/qqmlinfo.h>
-#include <QtCore/qnumeric.h>
-#include <QtCore/qmath.h>
-#include <math.h> // ::fmod
-
-#ifdef Q_CC_MSVC
-// MSVC2010 warns about 'unreferenced formal parameter', even if it's used in p->~T()
-# pragma warning( disable : 4100 )
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace QQmlJS;
-
-QQmlAbstractBinding::VTable QV4Bindings_Binding_vtable = {
- QV4Bindings::Binding::destroy,
- QQmlAbstractBinding::default_expression,
- QV4Bindings::Binding::propertyIndex,
- QV4Bindings::Binding::object,
- QV4Bindings::Binding::setEnabled,
- QV4Bindings::Binding::update,
- QV4Bindings::Binding::retargetBinding
-};
-
-namespace {
-
-// The highest bit is the sign bit, in any endianness
-static const quint64 doubleSignMask = (quint64(0x1) << 63);
-
-inline bool signBitSet(const double &v)
-{
- union { double d; quint64 u; } u;
- u.d = v;
- return (u.u & doubleSignMask);
-}
-
-inline double setSignBit(const double &v, bool b = true)
-{
- union { double d; quint64 u; } u;
-
- u.d = v;
- if (b) {
- u.u |= doubleSignMask;
- } else {
- u.u &= ~doubleSignMask;
- }
- return u.d;
-}
-
-inline double clearSignBit(const double &v, bool b = true)
-{
- return setSignBit(v, !b);
-}
-
-struct Register {
- typedef QQmlRegisterType Type;
-
- enum SpecialNumericValue {
- NegativeZero = 1,
- PositiveInfinity = 2,
- NegativeInfinity = 3,
- NotANumber = 4
- };
-
- inline void setUndefined() { dataType = UndefinedType; }
- inline bool isUndefined() const { return dataType == UndefinedType; }
-
- inline void setNull() { dataType = NullType; }
-
- inline void setNaN() { setnumber(qSNaN()); }
- inline void setNaNType() { dataType = SpecialNumericType; intValue = NotANumber; } // non-numeric representation of NaN
- inline bool isNaN() const { return (((dataType == SpecialNumericType) && (intValue == NotANumber)) ||
- ((dataType == NumberType) && qIsNaN(numberValue))); }
-
- inline void setInf(bool negative) { setnumber(setSignBit(qInf(), negative)); }
- inline void setInfType(bool negative) { dataType = SpecialNumericType; intValue = (negative ? NegativeInfinity : PositiveInfinity); } // non-numeric representation of Inf
- inline bool isInf() const { return (((dataType == SpecialNumericType) && ((intValue == NegativeInfinity) || (intValue == PositiveInfinity))) ||
- ((dataType == NumberType) && qIsInf(numberValue))); }
-
- inline void setNegativeZero() { setnumber(setSignBit(0)); }
- inline void setNegativeZeroType() { dataType = SpecialNumericType; intValue = NegativeZero; } // non-numeric representation of -0
- inline bool isNegativeZero() const { return (((dataType == SpecialNumericType) && (intValue == NegativeZero)) ||
- ((dataType == NumberType) && (numberValue == 0) && signBitSet(numberValue))); }
-
- inline void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; }
- inline QObject *getQObject() const { return qobjectValue; }
-
- inline void setnumber(double v) { numberValue = v; dataType = NumberType; }
- inline double getnumber() const { return numberValue; }
- inline double &getnumberref() { return numberValue; }
-
- inline void setfloat(float v) { floatValue = v; dataType = FloatType; }
- inline float getfloat() const { return floatValue; }
- inline float &getfloatref() { return floatValue; }
-
- inline void setint(int v) { intValue = v; dataType = IntType; }
- inline int getint() const { return intValue; }
- inline int &getintref() { return intValue; }
-
- inline void setbool(bool v) { boolValue = v; dataType = BoolType; }
- inline bool getbool() const { return boolValue; }
- inline bool &getboolref() { return boolValue; }
-
- inline QVariant *getvariantptr() { return reinterpret_cast<QVariant *>(typeDataPtr()); }
- inline QString *getstringptr() { return reinterpret_cast<QString *>(typeDataPtr()); }
- inline QUrl *geturlptr() { return reinterpret_cast<QUrl *>(typeDataPtr()); }
- inline v8::Handle<v8::Value> *gethandleptr() { return reinterpret_cast<v8::Handle<v8::Value> *>(typeDataPtr()); }
- inline QJSValue *getjsvalueptr() { return reinterpret_cast<QJSValue *>(typeDataPtr()); }
-
- inline const QVariant *getvariantptr() const { return reinterpret_cast<const QVariant *>(typeDataPtr()); }
- inline const QString *getstringptr() const { return reinterpret_cast<const QString *>(typeDataPtr()); }
- inline const QUrl *geturlptr() const { return reinterpret_cast<const QUrl *>(typeDataPtr()); }
- inline const v8::Handle<v8::Value> *gethandleptr() const { return reinterpret_cast<const v8::Handle<v8::Value> *>(typeDataPtr()); }
- inline const QJSValue *getjsvalueptr() const { return reinterpret_cast<const QJSValue *>(typeDataPtr()); }
-
- size_t dataSize() { return sizeof(data); }
- inline void *typeDataPtr() { return (void *)&data; }
- inline void *typeMemory() { return (void *)data; }
- inline const void *typeDataPtr() const { return (void *)&data; }
- inline const void *typeMemory() const { return (void *)data; }
-
- inline Type gettype() const { return dataType; }
- inline void settype(Type t) { dataType = t; }
-
- Type dataType; // Type of data
- union {
- QObject *qobjectValue;
- double numberValue;
- float floatValue;
- int intValue;
- bool boolValue;
- void *data[sizeof(QVariant)];
- qint64 q_for_alignment_1;
- double q_for_alignment_2;
- };
-
- inline void cleanup();
- inline void cleanupString();
- inline void cleanupUrl();
- inline void cleanupColor();
- inline void cleanupVariant();
- inline void cleanupHandle();
- inline void cleanupJSValue();
-
- inline void copy(const Register &other);
- inline void init(Type type);
-#ifdef REGISTER_CLEANUP_DEBUG
- Register() {
- type = 0;
- }
-
- ~Register() {
- if (dataType >= FirstCleanupType)
- qWarning("Register leaked of type %d", dataType);
- }
-#endif
-
- template <typename T>
- inline static void copyConstructPointee(T *p, const T *other)
- {
- new (p) T(*other);
- }
-
- template <typename T>
- inline static void defaultConstructPointee(T *p)
- {
- new (p) T();
- }
-
- template <typename T>
- inline static void destroyPointee(T *p)
- {
- p->~T();
- }
-};
-
-void Register::cleanup()
-{
- if (dataType >= FirstCleanupType) {
- if (dataType == QStringType) {
- destroyPointee(getstringptr());
- } else if (dataType == QUrlType) {
- destroyPointee(geturlptr());
- } else if (dataType == QColorType) {
- QQml_valueTypeProvider()->destroyValueType(QMetaType::QColor, typeDataPtr(), dataSize());
- } else if (dataType == QVariantType) {
- destroyPointee(getvariantptr());
- } else if (dataType == qMetaTypeId<v8::Handle<v8::Value> >()) {
- destroyPointee(gethandleptr());
- } else if (dataType == qMetaTypeId<QJSValue>()) {
- destroyPointee(getjsvalueptr());
- }
- }
- setUndefined();
-}
-
-void Register::cleanupString()
-{
- destroyPointee(getstringptr());
- setUndefined();
-}
-
-void Register::cleanupUrl()
-{
- destroyPointee(geturlptr());
- setUndefined();
-}
-
-void Register::cleanupColor()
-{
- QQml_valueTypeProvider()->destroyValueType(QMetaType::QColor, typeDataPtr(), dataSize());
- setUndefined();
-}
-
-void Register::cleanupVariant()
-{
- destroyPointee(getvariantptr());
- setUndefined();
-}
-
-void Register::cleanupHandle()
-{
- destroyPointee(gethandleptr());
- setUndefined();
-}
-
-void Register::cleanupJSValue()
-{
- destroyPointee(getjsvalueptr());
- setUndefined();
-}
-
-void Register::copy(const Register &other)
-{
- *this = other;
- if (other.dataType >= FirstCleanupType) {
- if (other.dataType == QStringType)
- copyConstructPointee(getstringptr(), other.getstringptr());
- else if (other.dataType == QUrlType)
- copyConstructPointee(geturlptr(), other.geturlptr());
- else if (other.dataType == QColorType)
- QQml_valueTypeProvider()->copyValueType(QMetaType::QColor, other.typeDataPtr(), typeDataPtr(), dataSize());
- else if (other.dataType == QVariantType)
- copyConstructPointee(getvariantptr(), other.getvariantptr());
- else if (other.dataType == qMetaTypeId<v8::Handle<v8::Value> >())
- copyConstructPointee(gethandleptr(), other.gethandleptr());
- else if (other.dataType == qMetaTypeId<QJSValue>())
- copyConstructPointee(getjsvalueptr(), other.getjsvalueptr());
- }
-}
-
-void Register::init(Type type)
-{
- dataType = type;
- if (dataType >= FirstCleanupType) {
- if (dataType == QStringType)
- defaultConstructPointee(getstringptr());
- else if (dataType == QUrlType)
- defaultConstructPointee(geturlptr());
- else if (dataType == QColorType)
- QQml_valueTypeProvider()->initValueType(QMetaType::QColor, typeDataPtr(), dataSize());
- else if (dataType == QVariantType)
- defaultConstructPointee(getvariantptr());
- else if (dataType == qMetaTypeId<v8::Handle<v8::Value> >())
- defaultConstructPointee(gethandleptr());
- else if (dataType == qMetaTypeId<QJSValue>())
- defaultConstructPointee(getjsvalueptr());
- }
-}
-
-} // end of anonymous namespace
-
-QV4Bindings::QV4Bindings(const char *programData, QQmlContextData *context)
-: subscriptions(0), program(0), bindings(0)
-{
- program = (QV4Program *)programData;
- if (program) {
- subscriptions = new Subscription[program->subscriptions];
- bindings = new Binding[program->bindings];
-
- QQmlAbstractExpression::setContext(context);
- }
-}
-
-QV4Bindings::~QV4Bindings()
-{
- delete [] bindings; bindings = 0;
- delete [] subscriptions; subscriptions = 0;
-}
-
-QQmlAbstractBinding *QV4Bindings::configBinding(QObject *target, QObject *scope,
- const QQmlInstruction::instr_assignV4Binding *i)
-{
- Binding *rv = bindings + i->value;
-
- rv->instruction = i;
- rv->target = target;
- rv->scope = scope;
- rv->parent = this;
-
- addref(); // This is decremented in Binding::destroy()
-
- return rv;
-}
-
-void QV4Bindings::Binding::setEnabled(QQmlAbstractBinding *_This,
- bool e, QQmlPropertyPrivate::WriteFlags flags)
-{
- QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This);
-
- if (This->enabledFlag() != e) {
- This->setEnabledFlag(e);
-
- if (e) update(_This, flags);
- }
-}
-
-void QV4Bindings::Binding::update(QQmlAbstractBinding *_This, QQmlPropertyPrivate::WriteFlags flags)
-{
- QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This);
- This->parent->run(This, flags);
-}
-
-void QV4Bindings::Binding::destroy(QQmlAbstractBinding *_This, QQmlAbstractBinding::DestroyMode mode)
-{
- QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This);
-
- if (mode == QQmlAbstractBinding::DisconnectBinding)
- This->disconnect();
-
- This->setEnabledFlag(false);
- This->removeFromObject();
- This->clear();
- This->removeError();
- This->parent->release();
-}
-
-int QV4Bindings::Binding::propertyIndex(const QQmlAbstractBinding *_This)
-{
- const QV4Bindings::Binding *This = static_cast<const QV4Bindings::Binding *>(_This);
-
- if (This->target.hasValue()) return This->target.constValue()->targetProperty;
- else return This->instruction->property;
-}
-
-QObject *QV4Bindings::Binding::object(const QQmlAbstractBinding *_This)
-{
- const QV4Bindings::Binding *This = static_cast<const QV4Bindings::Binding *>(_This);
-
- if (This->target.hasValue()) return This->target.constValue()->target;
- return *This->target;
-}
-
-void QV4Bindings::Binding::retargetBinding(QQmlAbstractBinding *_This, QObject *t, int i)
-{
- QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This);
-
- This->target.value().target = t;
- This->target.value().targetProperty = i;
-}
-
-void QV4Bindings::Binding::disconnect()
-{
- // We iterate over the signal table to find all subscriptions associated with this binding.
- // This is slow, but disconnect() is not called in the common case, only in special cases
- // like when the binding is overwritten.
- QV4Program * const program = parent->program;
- for (quint16 subIndex = 0; subIndex < program->subscriptions; subIndex++) {
- QV4Program::BindingReferenceList * const list = program->signalTable(subIndex);
- for (quint32 bindingIndex = 0; bindingIndex < list->count; ++bindingIndex) {
- QV4Program::BindingReference * const bindingRef = list->bindings + bindingIndex;
- Binding * const binding = parent->bindings + bindingRef->binding;
- if (binding == this) {
- Subscription * const sub = parent->subscriptions + subIndex;
- if (sub->active()) {
- sub->setActive(false);
- sub->disconnect();
- }
- }
- }
- }
-}
-
-void QV4Bindings::Binding::dump()
-{
- qWarning() << parent->context()->url << instruction->line << instruction->column;
-}
-
-QV4Bindings::Subscription::Subscription()
- : m_bindings(0)
-{
- setCallback(QQmlNotifierEndpoint::QV4BindingsSubscription);
-}
-
-int QV4Bindings::Subscription::method() const
-{
- Q_ASSERT(bindings() != 0);
- return (this - bindings()->subscriptions);
-}
-
-void QV4Bindings::Subscription::setBindings(QV4Bindings *bindings)
-{
- m_bindings = bindings;
-}
-
-QV4Bindings *QV4Bindings::Subscription::bindings() const
-{
- return *m_bindings;
-}
-
-bool QV4Bindings::Subscription::active() const
-{
- return m_bindings.flag();
-}
-
-void QV4Bindings::Subscription::setActive(bool active)
-{
- m_bindings.setFlagValue(active);
-}
-
-void QV4BindingsSubscription_callback(QQmlNotifierEndpoint *e, void **)
-{
- QV4Bindings::Subscription *s = static_cast<QV4Bindings::Subscription *>(e);
- Q_ASSERT(s->bindings());
- s->bindings()->subscriptionNotify(s->method());
-}
-
-void QV4Bindings::subscriptionNotify(int id)
-{
- QV4Program::BindingReferenceList *list = program->signalTable(id);
-
- for (quint32 ii = 0; ii < list->count; ++ii) {
- QV4Program::BindingReference *bindingRef = list->bindings + ii;
-
- Binding *binding = bindings + bindingRef->binding;
-
- if (binding->executedBlocks & bindingRef->blockMask) {
- run(binding, QQmlPropertyPrivate::DontRemoveBinding);
- }
- }
-}
-
-void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags)
-{
- if (!binding->enabledFlag())
- return;
-
- QQmlContextData *context = QQmlAbstractExpression::context();
- if (!context || !context->isValid())
- return;
-
- // Check that the target has not been deleted
- if (QQmlData::wasDeleted(*binding->target))
- return;
-
- QQmlTrace trace("V4 Binding Update");
- trace.addDetail("URL", context->url);
- trace.addDetail("Line", binding->instruction->line);
- trace.addDetail("Column", binding->instruction->column);
-
- QQmlBindingProfiler prof(context->urlString, binding->instruction->line, binding->instruction->column, QQmlProfilerService::V4Binding);
-
- const int propType = binding->instruction->propType;
- const int property = binding->instruction->property;
-
- if (binding->updatingFlag()) {
- QString name;
- if (propType) {
- QQmlValueType *vt = QQmlValueTypeFactory::valueType(propType);
- Q_ASSERT(vt);
-
- name = QLatin1String(binding->target->metaObject()->property(property & 0x0000FFFF).name());
- name.append(QLatin1Char('.'));
- name.append(QLatin1String(vt->metaObject()->property(property >> 16).name()));
- } else {
- name = QLatin1String(binding->target->metaObject()->property(property).name());
- }
- qmlInfo(*binding->target) << tr("Binding loop detected for property \"%1\"").arg(name);
- return;
- }
-
- int index = binding->instruction->value;
- int fallbackIndex = binding->instruction->fallbackValue;
-
- bool invalidated = false;
- bool *inv = (fallbackIndex != -1) ? &invalidated : 0;
-
- binding->setUpdatingFlag(true);
- if (propType) {
- QQmlValueType *vt = QQmlValueTypeFactory::valueType(propType);
- Q_ASSERT(vt);
- vt->read(*binding->target, property & 0x0000FFFF);
-
- QObject *target = vt;
- run(index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv);
-
- if (!invalidated) {
- vt->write(*binding->target, property & 0x0000FFFF, flags);
- }
- } else {
- QQmlData *data = QQmlData::get(*binding->target);
- QQmlPropertyData *propertyData = (data && data->propertyCache ? data->propertyCache->property(property) : 0);
-
- if (propertyData && propertyData->isVarProperty()) {
- // We will allocate a V8 handle in this conversion/store
- v8::HandleScope handle_scope;
- v8::Context::Scope context_scope(QQmlEnginePrivate::get(context->engine)->v8engine()->context());
-
- run(index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv);
- } else {
- run(index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv);
- }
- }
- binding->setUpdatingFlag(false);
-
- if (invalidated) {
- // This binding is no longer valid - fallback to V8
- Q_ASSERT(fallbackIndex > -1);
- QQmlAbstractBinding *b = QQmlPropertyPrivate::activateSharedBinding(context, fallbackIndex, flags);
- Q_ASSERT(b == binding);
- b->destroy();
- }
-}
-
-void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex)
-{
- Subscription *sub = (subscriptions + subIndex);
- sub->disconnect();
-
- if (p->idValues[idIndex]) {
- sub->setBindings(this);
- sub->connect(&p->idValues[idIndex].bindings);
- sub->setActive(true);
- } else {
- sub->setActive(false);
- }
-}
-
-void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngine *e)
-{
- Subscription *sub = (subscriptions + subIndex);
- if (sub->isConnected(o, notifyIndex))
- return;
- sub->setBindings(this);
- if (o) {
- sub->connect(o, notifyIndex, e);
- sub->setActive(true);
- } else {
- sub->disconnect();
- sub->setActive(false);
- }
-}
-
-static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4)
-{
- QVariant qtscript = qtscriptRaw;
-
- if (qtscript.userType() == v4.userType()) {
- } else if (qtscript.canConvert(v4.userType())) {
- qtscript.convert(v4.userType());
- } else if (qtscript.userType() == QVariant::Invalid && v4.userType() == QMetaType::QObjectStar) {
- qtscript = qVariantFromValue<QObject *>(0);
- } else {
- return false;
- }
-
- int type = qtscript.userType();
-
- if (type == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
- return QQmlMetaType::QQuickAnchorLineCompare(qtscript.constData(), v4.constData());
- } else if (type == QMetaType::Double) {
-
- double la = qvariant_cast<double>(qtscript);
- double lr = qvariant_cast<double>(v4);
-
- return la == lr || (qIsNaN(la) && qIsNaN(lr));
-
- } else if (type == QMetaType::Float) {
-
- float la = qvariant_cast<float>(qtscript);
- float lr = qvariant_cast<float>(v4);
-
- return la == lr || (qIsNaN(la) && qIsNaN(lr));
-
- } else {
- return qtscript == v4;
- }
-}
-
-QByteArray testResultToString(const QVariant &result, bool undefined)
-{
- if (undefined) {
- return "undefined";
- } else {
- QString rv;
- QDebug d(&rv);
- d << result;
- return rv.toUtf8();
- }
-}
-
-static void testBindingResult(const QString &binding, quint16 line, quint16 column,
- QQmlContextData *context, QObject *scope,
- const Register &result, int resultType)
-{
- QQmlExpression expression(context->asQQmlContext(), scope, binding);
- bool isUndefined = false;
- QVariant value = expression.evaluate(&isUndefined);
-
- bool iserror = false;
- QByteArray qtscriptResult;
- QByteArray v4Result;
-
- const int handleType = qMetaTypeId<v8::Handle<v8::Value> >();
-
- if (expression.hasError()) {
- iserror = true;
- qtscriptResult = "exception";
- } else if ((value.userType() != resultType) &&
- (resultType != QMetaType::QVariant) &&
- (resultType != qMetaTypeId<QJSValue>()) &&
- (resultType != handleType)) {
- // Override the QMetaType conversions to make them more JS friendly.
- if (value.userType() == QMetaType::Double && (resultType == QMetaType::QString ||
- resultType == QMetaType::QUrl)) {
- // number to string-like conversion.
- value = QVariant::fromValue<QString>(QString::number(value.toDouble(), 'g', 16));
- } else if (value.userType() == QMetaType::QUrl && resultType == QMetaType::Bool) {
- // url to bool conversion
- value = QVariant::fromValue<bool>(!value.toUrl().isEmpty());
- }
-
- if (!value.isNull() && !value.convert(resultType)) {
- iserror = true;
- qtscriptResult = "exception";
- } else if (resultType == QMetaType::QUrl) {
- // a V8 value was converted to QUrl.
- value = QVariant::fromValue<QUrl>(context->resolvedUrl(value.toUrl()));
- }
- }
-
- if (! iserror)
- qtscriptResult = testResultToString(value, isUndefined);
-
- if (isUndefined && result.isUndefined()) {
- return;
- } else if(isUndefined != result.isUndefined()) {
- iserror = true;
- }
-
- QVariant v4value;
- if (!result.isUndefined()) {
- switch (resultType) {
- case QMetaType::QString:
- v4value = *result.getstringptr();
- break;
- case QMetaType::QUrl:
- v4value = *result.geturlptr();
- break;
- case QMetaType::QObjectStar:
- v4value = qVariantFromValue<QObject *>(result.getQObject());
- break;
- case QMetaType::Bool:
- v4value = result.getbool();
- break;
- case QMetaType::Int:
- v4value = result.getint();
- break;
- case QMetaType::Double:
- v4value = result.getnumber();
- break;
- case QMetaType::QColor:
- v4value = QVariant(QMetaType::QColor, result.typeDataPtr());
- break;
- case QMetaType::QVariant:
- v4value = *result.getvariantptr();
- break;
- default:
- if (resultType == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
- v4value = QVariant(QQmlMetaType::QQuickAnchorLineMetaTypeId(), result.typeDataPtr());
- } else if (resultType == qMetaTypeId<QJSValue>()) {
- v4value = result.getjsvalueptr()->toVariant();
- } else if (resultType == handleType) {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- v4value = ep->v8engine()->toVariant(*result.gethandleptr(), resultType);
- } else {
- iserror = true;
- v4Result = "Unknown V4 type";
- }
- }
- }
- if (v4Result.isEmpty())
- v4Result = testResultToString(v4value, result.isUndefined());
-
- if (!testCompareVariants(value, v4value))
- iserror = true;
-
- if (iserror) {
- qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ':' << line << ':' << column;
-
- qWarning().nospace() << " Binding: " << binding;
- qWarning().nospace() << " QtScript: " << qtscriptResult.constData();
- qWarning().nospace() << " V4: " << v4Result.constData();
- }
-}
-
-static void testBindingException(const QString &binding, quint16 line, quint16 column,
- QQmlContextData *context, QObject *scope)
-{
- QQmlExpression expression(context->asQQmlContext(), scope, binding);
- bool isUndefined = false;
- QVariant value = expression.evaluate(&isUndefined);
-
- if (!expression.hasError()) {
- QByteArray qtscriptResult = testResultToString(value, isUndefined);
- qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ':' << line << ':' << column;
- qWarning().nospace() << " Binding: " << binding;
- qWarning().nospace() << " QtScript: " << qtscriptResult.constData();
- qWarning().nospace() << " V4: exception";
- }
-}
-
-static void throwException(int id, QQmlDelayedError *error,
- QV4Program *program, QQmlContextData *context,
- const QString &description = QString())
-{
- if (description.isEmpty())
- error->setErrorDescription(QLatin1String("TypeError: Result of expression is not an object"));
- else
- error->setErrorDescription(description);
- error->setErrorObject(context->contextObject);
- if (id != 0xFF) {
- quint32 e = *((quint32 *)(program->data() + program->exceptionDataOffset) + id);
- error->setErrorLocation(context->url, (e >> 16), (e & 0xFFFF));
- } else {
- error->setErrorLocation(context->url, -1, -1);
- }
- if (!context->engine || !error->addError(QQmlEnginePrivate::get(context->engine)))
- QQmlEnginePrivate::warning(context->engine, error);
-}
-
-const double QV4Bindings::D32 = 4294967296.0;
-
-qint32 QV4Bindings::toInt32(double n)
-{
- if (qIsNaN(n) || qIsInf(n) || (n == 0))
- return 0;
-
- double sign = (n < 0) ? -1.0 : 1.0;
- double abs_n = fabs(n);
-
- n = ::fmod(sign * ::floor(abs_n), D32);
- const double D31 = D32 / 2.0;
-
- if (sign == -1 && n < -D31)
- n += D32;
-
- else if (sign != -1 && n >= D31)
- n -= D32;
-
- return qint32 (n);
-}
-
-inline quint32 QV4Bindings::toUint32(double n)
-{
- if (qIsNaN(n) || qIsInf(n) || (n == 0))
- return 0;
-
- double sign = (n < 0) ? -1.0 : 1.0;
- double abs_n = fabs(n);
-
- n = ::fmod(sign * ::floor(abs_n), D32);
-
- if (n < 0)
- n += D32;
-
- return quint32 (n);
-}
-
-#define THROW_EXCEPTION_STR(id, str) { \
- if (testBinding) testBindingException(*testBindingSource, bindingLine, bindingColumn, context, scope); \
- throwException((id), error, program, context, (str)); \
- goto exceptionExit; \
-}
-
-#define THROW_VALUE_EXCEPTION_STR(id, str) { \
- throwException((id), error, program, context, (str)); \
- goto exceptionExit; \
-}
-
-#define THROW_EXCEPTION(id) THROW_EXCEPTION_STR(id, QString())
-
-#define MARK_REGISTER(reg) cleanupRegisterMask |= (1 << (reg))
-#define MARK_CLEAN_REGISTER(reg) cleanupRegisterMask &= ~(1 << (reg))
-
-#define STRING_REGISTER(reg) { \
- registers[(reg)].settype(QStringType); \
- MARK_REGISTER(reg); \
-}
-
-#define URL_REGISTER(reg) { \
- registers[(reg)].settype(QUrlType); \
- MARK_REGISTER(reg); \
-}
-
-#define COLOR_REGISTER(reg) { \
- registers[(reg)].settype(QColorType); \
- MARK_REGISTER(reg); \
-}
-
-#define VARIANT_REGISTER(reg) { \
- registers[(reg)].settype(QVariantType); \
- MARK_REGISTER(reg); \
-}
-
-#define V8HANDLE_REGISTER(reg) { \
- registers[(reg)].settype(V8HandleType); \
- MARK_REGISTER(reg); \
-}
-
-#define JSVALUE_REGISTER(reg) { \
- registers[(reg)].settype(QJSValueType); \
- MARK_REGISTER(reg); \
-}
-
-namespace {
-
-bool bindingInvalidated(bool *invalidated, QObject *obj, QQmlContextData *context, int index)
-{
- if (invalidated != 0) {
- if (QQmlData *data = QQmlData::get(obj, true)) {
- if (!data->propertyCache) {
- data->propertyCache = QQmlEnginePrivate::get(context->engine)->cache(obj);
- if (data->propertyCache) data->propertyCache->addref();
- }
-
- if (QQmlPropertyData *prop = data->propertyCache ? data->propertyCache->property(index) : 0) {
- if (prop->isOverridden()) {
- // TODO: avoid construction of name and name-based lookup
- int resolvedIndex = data->propertyCache->property(prop->name(obj), obj, context)->coreIndex;
- if (index < resolvedIndex) {
- *invalidated = true;
- return true;
- }
- }
- }
- }
- }
-
- return false;
-}
-
-}
-
-#ifdef QML_THREADED_INTERPRETER
-void **QV4Bindings::getDecodeInstrTable()
-{
- static void **decode_instr;
- if (!decode_instr) {
- QV4Bindings *dummy = new QV4Bindings(0, 0);
- quint32 executedBlocks = 0;
- dummy->run(0, executedBlocks, 0, 0, 0, 0,
- QQmlPropertyPrivate::BypassInterceptor,
- 0, &decode_instr);
- dummy->release();
- }
- return decode_instr;
-}
-#endif
-
-void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
- QQmlContextData *context, QQmlDelayedError *error,
- QObject *scope, QObject *output,
- QQmlPropertyPrivate::WriteFlags storeFlags,
- bool *invalidated
-#ifdef QML_THREADED_INTERPRETER
- ,void ***table
-#endif
- )
-{
-#ifdef QML_THREADED_INTERPRETER
- if (table) {
- static void *decode_instr[] = {
- FOR_EACH_V4_INSTR(QML_V4_INSTR_ADDR)
- };
-
- *table = decode_instr;
- return;
- }
-#endif
-
-
- error->removeError();
-
- Register registers[32];
- quint32 cleanupRegisterMask = 0;
-
- executedBlocks = 0;
-
- const char *code = program->instructions();
- code += instrIndex * QML_V4_INSTR_SIZE(Jump, jump);
- const V4Instr *instr = reinterpret_cast<const V4Instr *>(code);
-
- const char *data = program->data();
-
- QString *testBindingSource = 0;
- bool testBinding = false;
- int bindingLine = 0;
- int bindingColumn = 0;
-
-#ifdef QML_THREADED_INTERPRETER
- goto *instr->common.code;
-#else
- for (;;) {
- switch (instr->common.type) {
-#endif
-
- QML_V4_BEGIN_INSTR(Noop, common)
- QML_V4_END_INSTR(Noop, common)
-
- QML_V4_BEGIN_INSTR(BindingId, id)
- bindingLine = instr->id.line;
- bindingColumn = instr->id.column;
- QML_V4_END_INSTR(BindingId, id)
-
- QML_V4_BEGIN_INSTR(SubscribeId, subscribeop)
- subscribeId(context, instr->subscribeop.index, instr->subscribeop.offset);
- QML_V4_END_INSTR(SubscribeId, subscribeop)
-
- QML_V4_BEGIN_INSTR(FetchAndSubscribe, fetchAndSubscribe)
- {
- Register &reg = registers[instr->fetchAndSubscribe.reg];
-
- if (reg.isUndefined())
- THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId);
-
- QObject *object = reg.getQObject();
- if (!object) {
- THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId);
- } else {
- if (bindingInvalidated(invalidated, object, context, instr->fetchAndSubscribe.property.coreIndex))
- goto programExit;
-
- const Register::Type valueType = (Register::Type)instr->fetchAndSubscribe.valueType;
- reg.init(valueType);
- if (instr->fetchAndSubscribe.valueType >= FirstCleanupType)
- MARK_REGISTER(instr->fetchAndSubscribe.reg);
-
- QQmlData::flushPendingBinding(object, instr->fetchAndSubscribe.property.coreIndex);
-
- QQmlAccessors *accessors = instr->fetchAndSubscribe.property.accessors;
- accessors->read(object, instr->fetchAndSubscribe.property.accessorData,
- reg.typeDataPtr());
-
- if (valueType == FloatType) {
- // promote floats
- const double v = reg.getfloat();
- reg.setnumber(v);
- }
-
- if (accessors->notifier) {
- QQmlNotifier *notifier = 0;
- accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, &notifier);
- if (notifier) {
- int subIdx = instr->fetchAndSubscribe.subscription;
- Subscription *sub = 0;
- if (subIdx != -1) {
- sub = (subscriptions + subIdx);
- sub->setBindings(this);
- }
- sub->connect(notifier);
- }
- } else {
- const int notifyIndex = instr->fetchAndSubscribe.property.notifyIndex;
- if (notifyIndex != -1) {
- const int subIdx = instr->fetchAndSubscribe.subscription;
- subscribe(object, notifyIndex, subIdx, context->engine);
- }
- }
- }
- }
- QML_V4_END_INSTR(FetchAndSubscribe, fetchAndSubscribe)
-
- QML_V4_BEGIN_INSTR(LoadId, load)
- registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data());
- QML_V4_END_INSTR(LoadId, load)
-
- QML_V4_BEGIN_INSTR(LoadScope, load)
- registers[instr->load.reg].setQObject(scope);
- QML_V4_END_INSTR(LoadScope, load)
-
- QML_V4_BEGIN_INSTR(LoadRoot, load)
- registers[instr->load.reg].setQObject(context->contextObject);
- QML_V4_END_INSTR(LoadRoot, load)
-
- QML_V4_BEGIN_INSTR(LoadSingletonObject, load)
- {
- Register &reg = registers[instr->load.reg];
-
- const QString *name = reg.getstringptr();
- QQmlTypeNameCache::Result r = context->imports->query(*name);
- reg.cleanupString();
-
- if (r.isValid() && r.type) {
- if (r.type->isSingleton()) {
- QQmlEngine *e = context->engine;
- QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo();
- siinfo->init(e); // note: this will also create QJSValue singleton, which is not strictly required here.
- QObject *qobjectSingleton = siinfo->qobjectApi(e);
- if (qobjectSingleton)
- reg.setQObject(qobjectSingleton);
- }
- }
- }
- QML_V4_END_INSTR(LoadSingletonObject, load)
-
- QML_V4_BEGIN_INSTR(LoadAttached, attached)
- {
- const Register &input = registers[instr->attached.reg];
- Register &output = registers[instr->attached.output];
- if (input.isUndefined())
- THROW_EXCEPTION(instr->attached.exceptionId);
-
- QObject *object = registers[instr->attached.reg].getQObject();
- if (!object) {
- output.setUndefined();
- } else {
- QObject *attached = qmlAttachedPropertiesObjectById(instr->attached.id, input.getQObject(), true);
- Q_ASSERT(attached);
- output.setQObject(attached);
- }
- }
- QML_V4_END_INSTR(LoadAttached, attached)
-
- QML_V4_BEGIN_INSTR(UnaryNot, unaryop)
- {
- registers[instr->unaryop.output].setbool(!registers[instr->unaryop.src].getbool());
- }
- QML_V4_END_INSTR(UnaryNot, unaryop)
-
- QML_V4_BEGIN_INSTR(UnaryMinusNumber, unaryop)
- {
- registers[instr->unaryop.output].setnumber(-registers[instr->unaryop.src].getnumber());
- }
- QML_V4_END_INSTR(UnaryMinusNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(UnaryMinusInt, unaryop)
- {
- registers[instr->unaryop.output].setint(-registers[instr->unaryop.src].getint());
- }
- QML_V4_END_INSTR(UnaryMinusInt, unaryop)
-
- QML_V4_BEGIN_INSTR(UnaryPlusNumber, unaryop)
- {
- registers[instr->unaryop.output].setnumber(+registers[instr->unaryop.src].getnumber());
- }
- QML_V4_END_INSTR(UnaryPlusNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(UnaryPlusInt, unaryop)
- {
- registers[instr->unaryop.output].setint(+registers[instr->unaryop.src].getint());
- }
- QML_V4_END_INSTR(UnaryPlusInt, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToInt, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setint(src.getbool());
- }
- QML_V4_END_INSTR(ConvertBoolToInt, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getjsvalueptr()) QJSValue(src.getbool());
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertBoolToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setnumber(src.getbool());
- }
- QML_V4_END_INSTR(ConvertBoolToNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToString, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getstringptr()) QString(QLatin1String(src.getbool() ? "true" : "false"));
- STRING_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertBoolToString, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getvariantptr()) QVariant(src.getbool());
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertBoolToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertBoolToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Boolean::New(src.getbool()));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertBoolToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setbool(src.getint());
- }
- QML_V4_END_INSTR(ConvertIntToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getjsvalueptr()) QJSValue(src.getint());
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertIntToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else if (src.isNaN()) output.setNaN();
- else if (src.isInf()) output.setInf(src.getint() == Register::NegativeInfinity);
- else if (src.isNegativeZero()) output.setNegativeZero();
- else output.setnumber(double(src.getint()));
- }
- QML_V4_END_INSTR(ConvertIntToNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToString, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getstringptr()) QString(QString::number(src.getint()));
- STRING_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertIntToString, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getvariantptr()) QVariant(src.getint());
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertIntToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertIntToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Integer::New(src.getint()));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertIntToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertJSValueToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QJSValue tmp(*src.getjsvalueptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupJSValue();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- if (tmp.isUndefined()) {
- output.setUndefined();
- } else {
- QV8Engine *v8engine = QQmlEnginePrivate::get(context->engine)->v8engine();
- new (output.gethandleptr()) v8::Handle<v8::Value>(
- QJSValuePrivate::get(tmp)->asV8Value(v8engine));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- }
- QML_V4_END_INSTR(ConvertJSValueToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setbool(src.getnumber() != 0);
- }
- QML_V4_END_INSTR(ConvertNumberToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToInt, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setint(toInt32(src.getnumber()));
- }
- QML_V4_END_INSTR(ConvertNumberToInt, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getjsvalueptr()) QJSValue(src.getnumber());
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertNumberToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToString, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getstringptr()) QString(QString::number(src.getnumber(), 'g', 16));
- STRING_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertNumberToString, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.getvariantptr()) QVariant(src.getnumber());
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertNumberToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNumberToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Number::New(src.getnumber()));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertNumberToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine.
- // Ideally we should just call the methods in the QScript namespace directly.
- QJSValue tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- output.setbool(tmp.toBool());
- }
- }
- QML_V4_END_INSTR(ConvertStringToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToInt, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine.
- // Ideally we should just call the methods in the QScript namespace directly.
- QJSValue tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- output.setint(tmp.toInt());
- }
- }
- QML_V4_END_INSTR(ConvertStringToInt, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QString tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getjsvalueptr()) QJSValue(tmp);
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertStringToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine.
- // Ideally we should just call the methods in the QScript namespace directly.
- QJSValue tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- output.setnumber(tmp.toNumber());
- }
- }
- QML_V4_END_INSTR(ConvertStringToNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToUrl, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QString tmp(*src.getstringptr());
- // Encoded dir-separators defeat QUrl processing - decode them first
- tmp.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.geturlptr()) QUrl(tmp);
-
- URL_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertStringToUrl, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToColor, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QString tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- QQml_valueTypeProvider()->createValueFromString(QMetaType::QColor, tmp, output.typeDataPtr(), output.dataSize());
-
- COLOR_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertStringToColor, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QString tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getvariantptr()) QVariant(tmp);
-
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertStringToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertStringToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QString tmp(*src.getstringptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupString();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.gethandleptr()) v8::Handle<v8::Value>(QJSConverter::toString(tmp));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertStringToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertUrlToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupUrl();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- output.setbool(!tmp.isEmpty());
- }
- }
- QML_V4_END_INSTR(ConvertUrlToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertUrlToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupUrl();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getjsvalueptr()) QJSValue(tmp.toString());
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertUrlToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertUrlToString, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupUrl();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getstringptr()) QString(tmp.toString());
- STRING_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertUrlToString, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertUrlToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupUrl();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getvariantptr()) QVariant(tmp);
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertUrlToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertUrlToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupUrl();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.gethandleptr()) v8::Handle<v8::Value>(QJSConverter::toString(tmp.toString()));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertUrlToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertColorToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- // for compatibility with color behavior in v8, always true
- output.setbool(true);
- }
- }
- QML_V4_END_INSTR(ConvertColorToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertColorToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QVariant tmp(QMetaType::QColor, src.typeDataPtr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupColor();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
-
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- QV8Engine *v8engine = ep->v8engine();
- QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor);
- v8::HandleScope handle_scope;
- v8::Context::Scope scope(v8engine->context());
- new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal(
- v8engine->valueTypeWrapper()->newValueType(tmp, vt)));
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertColorToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertColorToString, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QQml_valueTypeProvider()->createStringFromValue(QMetaType::QColor, src.typeDataPtr(), output.getstringptr());
- STRING_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertColorToString, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertColorToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QVariant tmp(QMetaType::QColor, src.typeDataPtr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupColor();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- new (output.getvariantptr()) QVariant(tmp);
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertColorToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertColorToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QVariant tmp(QMetaType::QColor, src.typeDataPtr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupColor();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
-
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor);
- new (output.gethandleptr()) v8::Handle<v8::Value>(ep->v8engine()->valueTypeWrapper()->newValueType(tmp, vt));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertColorToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertObjectToBool, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined())
- output.setUndefined();
- else
- output.setbool(src.getQObject() != 0);
- }
- QML_V4_END_INSTR(ConvertObjectToBool, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertObjectToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- v8::HandleScope handle_scope;
- v8::Context::Scope scope(ep->v8engine()->context());
- new (output.getjsvalueptr()) QJSValue(context->engine->newQObject(src.getQObject()));
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertObjectToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertObjectToVariant, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined())
- output.setUndefined();
- else {
- new (output.getvariantptr()) QVariant(qVariantFromValue<QObject *>(src.getQObject()));
- VARIANT_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertObjectToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertObjectToVar, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- // ### NaN
- if (src.isUndefined())
- output.setUndefined();
- else {
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
- new (output.gethandleptr()) v8::Handle<v8::Value>(ep->v8engine()->newQObject(src.getQObject()));
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertObjectToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertVarToJSValue, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- v8::Handle<v8::Value> tmp(*src.gethandleptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- output.cleanupHandle();
- MARK_CLEAN_REGISTER(instr->unaryop.output);
- }
- QV8Engine *v8engine = QQmlEnginePrivate::get(context->engine)->v8engine();
- new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal(tmp));
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- }
- QML_V4_END_INSTR(ConvertVarToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNullToJSValue, unaryop)
- {
- Register &output = registers[instr->unaryop.output];
- new (output.getjsvalueptr()) QJSValue(QJSValue::NullValue);
- JSVALUE_REGISTER(instr->unaryop.output);
- }
- QML_V4_END_INSTR(ConvertNullToJSValue, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNullToObject, unaryop)
- {
- Register &output = registers[instr->unaryop.output];
- output.setQObject(0);
- }
- QML_V4_END_INSTR(ConvertNullToObject, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNullToVariant, unaryop)
- {
- Register &output = registers[instr->unaryop.output];
- new (output.getvariantptr()) QVariant();
- VARIANT_REGISTER(instr->unaryop.output);
- }
- QML_V4_END_INSTR(ConvertNullToVariant, unaryop)
-
- QML_V4_BEGIN_INSTR(ConvertNullToVar, unaryop)
- {
- Register &output = registers[instr->unaryop.output];
- new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Null());
- V8HANDLE_REGISTER(instr->unaryop.output);
- }
- QML_V4_END_INSTR(ConvertNullToVar, unaryop)
-
- QML_V4_BEGIN_INSTR(ResolveUrl, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) {
- output.setUndefined();
- } else {
- const QUrl tmp(*src.geturlptr());
- if (instr->unaryop.src == instr->unaryop.output) {
- *output.geturlptr() = context->resolvedUrl(tmp);
- } else {
- new (output.geturlptr()) QUrl(context->resolvedUrl(tmp));
- URL_REGISTER(instr->unaryop.output);
- }
- }
- }
- QML_V4_END_INSTR(ResolveUrl, unaryop)
-
- QML_V4_BEGIN_INSTR(MathSinNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setnumber(qSin(src.getnumber()));
- }
- QML_V4_END_INSTR(MathSinNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathCosNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setnumber(qCos(src.getnumber()));
- }
- QML_V4_END_INSTR(MathCosNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathAbsNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setnumber(clearSignBit(qAbs(src.getnumber())));
- }
- QML_V4_END_INSTR(MathAbsNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathRoundNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined()) output.setUndefined();
- else output.setint(qRound(src.getnumber()));
- }
- QML_V4_END_INSTR(MathRoundNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathFloorNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined())
- output.setUndefined();
- else if (src.isNaN())
- // output should be an int, but still NaN
- output.setNaNType();
- else if (src.isInf())
- // output should be an int, but still Inf
- output.setInfType(signBitSet(src.getnumber()));
- else if (src.isNegativeZero())
- // output should be an int, but still -0
- output.setNegativeZeroType();
- else
- output.setint(qFloor(src.getnumber()));
- }
- QML_V4_END_INSTR(MathFloorNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathCeilNumber, unaryop)
- {
- const Register &src = registers[instr->unaryop.src];
- Register &output = registers[instr->unaryop.output];
- if (src.isUndefined())
- output.setUndefined();
- else if (src.isNaN())
- // output should be an int, but still NaN
- output.setNaNType();
- else if (src.isInf())
- // output should be an int, but still Inf
- output.setInfType(signBitSet(src.getnumber()));
- else if (src.isNegativeZero())
- // output should be an int, but still -0
- output.setNegativeZeroType();
- else {
- // Ensure that we preserve the sign bit (Math.ceil(-0) -> -0)
- const double input = src.getnumber();
- const int ceiled = qCeil(input);
- if (ceiled == 0 && signBitSet(input)) {
- output.setNegativeZeroType();
- } else {
- output.setint(ceiled);
- }
- }
- }
- QML_V4_END_INSTR(MathCeilNumber, unaryop)
-
- QML_V4_BEGIN_INSTR(MathPINumber, unaryop)
- {
- static const double qmlPI = 2.0 * qAsin(1.0);
- Register &output = registers[instr->unaryop.output];
- output.setnumber(qmlPI);
- }
- QML_V4_END_INSTR(MathPINumber, unaryop)
-
- QML_V4_BEGIN_INSTR(LoadNull, null_value)
- registers[instr->null_value.reg].setNull();
- QML_V4_END_INSTR(LoadNull, null_value)
-
- QML_V4_BEGIN_INSTR(LoadNumber, number_value)
- registers[instr->number_value.reg].setnumber(instr->number_value.value);
- QML_V4_END_INSTR(LoadNumber, number_value)
-
- QML_V4_BEGIN_INSTR(LoadInt, int_value)
- registers[instr->int_value.reg].setint(instr->int_value.value);
- QML_V4_END_INSTR(LoadInt, int_value)
-
- QML_V4_BEGIN_INSTR(LoadBool, bool_value)
- registers[instr->bool_value.reg].setbool(instr->bool_value.value);
- QML_V4_END_INSTR(LoadBool, bool_value)
-
- QML_V4_BEGIN_INSTR(LoadString, string_value)
- {
- Register &output = registers[instr->string_value.reg];
- QChar *string = (QChar *)(data + instr->string_value.offset);
- new (output.getstringptr()) QString(string, instr->string_value.length);
- STRING_REGISTER(instr->string_value.reg);
- }
- QML_V4_END_INSTR(LoadString, string_value)
-
- QML_V4_BEGIN_INSTR(EnableV4Test, string_value)
- {
- testBindingSource = new QString((QChar *)(data + instr->string_value.offset), instr->string_value.length);
- testBinding = true;
- }
- QML_V4_END_INSTR(String, string_value)
-
- QML_V4_BEGIN_INSTR(BitAndInt, binaryop)
- {
- registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() &
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(BitAndInt, binaryop)
-
- QML_V4_BEGIN_INSTR(BitOrInt, binaryop)
- {
- registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() |
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(BitAndInt, binaryop)
-
- QML_V4_BEGIN_INSTR(BitXorInt, binaryop)
- {
- registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() ^
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(BitXorInt, binaryop)
-
- QML_V4_BEGIN_INSTR(AddNumber, binaryop)
- {
- registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() +
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(AddNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(AddString, binaryop)
- {
- QString &string = *registers[instr->binaryop.output].getstringptr();
- if (instr->binaryop.output == instr->binaryop.left) {
- string += registers[instr->binaryop.right].getstringptr();
- } else {
- string = *registers[instr->binaryop.left].getstringptr() +
- *registers[instr->binaryop.right].getstringptr();
- }
- }
- QML_V4_END_INSTR(AddString, binaryop)
-
- QML_V4_BEGIN_INSTR(SubNumber, binaryop)
- {
- registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() -
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(SubNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(MulNumber, binaryop)
- {
- registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() *
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(MulNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(DivNumber, binaryop)
- {
- registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() /
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(DivNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(ModNumber, binaryop)
- {
- Register &target = registers[instr->binaryop.output];
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- target.setnumber(::fmod(left.getnumber(), right.getnumber()));
- }
- QML_V4_END_INSTR(ModInt, binaryop)
-
- QML_V4_BEGIN_INSTR(LShiftInt, binaryop)
- {
- registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() <<
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(LShiftInt, binaryop)
-
- QML_V4_BEGIN_INSTR(RShiftInt, binaryop)
- {
- registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() >>
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(RShiftInt, binaryop)
-
- QML_V4_BEGIN_INSTR(URShiftInt, binaryop)
- {
- registers[instr->binaryop.output].setint((unsigned)registers[instr->binaryop.left].getint() >>
- registers[instr->binaryop.right].getint());
- }
- QML_V4_END_INSTR(URShiftInt, binaryop)
-
- QML_V4_BEGIN_INSTR(GtNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() >
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(GtNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(LtNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() <
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(LtNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(GeNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() >=
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(GeNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(LeNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() <=
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(LeNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(EqualNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() ==
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(EqualNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(NotEqualNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() !=
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(NotEqualNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictEqualNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() ==
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(StrictEqualNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictNotEqualNumber, binaryop)
- {
- registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() !=
- registers[instr->binaryop.right].getnumber());
- }
- QML_V4_END_INSTR(StrictNotEqualNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(GtString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a > b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(GtString, binaryop)
-
- QML_V4_BEGIN_INSTR(LtString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a < b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(LtString, binaryop)
-
- QML_V4_BEGIN_INSTR(GeString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a >= b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(GeString, binaryop)
-
- QML_V4_BEGIN_INSTR(LeString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a <= b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(LeString, binaryop)
-
- QML_V4_BEGIN_INSTR(EqualString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a == b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(EqualString, binaryop)
-
- QML_V4_BEGIN_INSTR(NotEqualString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a != b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(NotEqualString, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictEqualString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a == b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(StrictEqualString, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictNotEqualString, binaryop)
- {
- const QString &a = *registers[instr->binaryop.left].getstringptr();
- const QString &b = *registers[instr->binaryop.right].getstringptr();
- bool result = a != b;
- if (instr->binaryop.left == instr->binaryop.output) {
- registers[instr->binaryop.output].cleanupString();
- MARK_CLEAN_REGISTER(instr->binaryop.output);
- }
- registers[instr->binaryop.output].setbool(result);
- }
- QML_V4_END_INSTR(StrictNotEqualString, binaryop)
-
- QML_V4_BEGIN_INSTR(EqualObject, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject();
- QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject();
- registers[instr->binaryop.output].setbool(leftobj == rightobj);
- }
- QML_V4_END_INSTR(EqualObject, binaryop)
-
- QML_V4_BEGIN_INSTR(NotEqualObject, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject();
- QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject();
- registers[instr->binaryop.output].setbool(leftobj != rightobj);
- }
- QML_V4_END_INSTR(NotEqualObject, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictEqualObject, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject();
- QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject();
- registers[instr->binaryop.output].setbool(leftobj == rightobj);
- }
- QML_V4_END_INSTR(StrictEqualObject, binaryop)
-
- QML_V4_BEGIN_INSTR(StrictNotEqualObject, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject();
- QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject();
- registers[instr->binaryop.output].setbool(leftobj != rightobj);
- }
- QML_V4_END_INSTR(StrictNotEqualObject, binaryop)
-
- QML_V4_BEGIN_INSTR(MathMaxNumber, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- Register &output = registers[instr->binaryop.output];
- if (left.isUndefined() || right.isUndefined()) {
- output.setUndefined();
- } else {
- const double lhs = left.getnumber();
- const double rhs = right.getnumber();
- double result(lhs);
- if (lhs == rhs) {
- // If these are both zero, +0 is greater than -0
- if (signBitSet(lhs) && !signBitSet(rhs))
- result = rhs;
- } else {
- result = qMax(lhs, rhs);
- }
- output.setnumber(result);
- }
- }
- QML_V4_END_INSTR(MathMaxNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(MathMinNumber, binaryop)
- {
- const Register &left = registers[instr->binaryop.left];
- const Register &right = registers[instr->binaryop.right];
- Register &output = registers[instr->binaryop.output];
- if (left.isUndefined() || right.isUndefined()) {
- output.setUndefined();
- } else {
- const double lhs = left.getnumber();
- const double rhs = right.getnumber();
- double result(lhs);
- if (lhs == rhs) {
- // If these are both zero, -0 is lesser than +0
- if (!signBitSet(lhs) && signBitSet(rhs))
- result = rhs;
- } else {
- result = qMin(lhs, rhs);
- }
- output.setnumber(result);
- }
- }
- QML_V4_END_INSTR(MathMinNumber, binaryop)
-
- QML_V4_BEGIN_INSTR(NewString, construct)
- {
- Register &output = registers[instr->construct.reg];
- new (output.getstringptr()) QString;
- STRING_REGISTER(instr->construct.reg);
- }
- QML_V4_END_INSTR(NewString, construct)
-
- QML_V4_BEGIN_INSTR(NewUrl, construct)
- {
- Register &output = registers[instr->construct.reg];
- new (output.geturlptr()) QUrl;
- URL_REGISTER(instr->construct.reg);
- }
- QML_V4_END_INSTR(NewUrl, construct)
-
- QML_V4_BEGIN_INSTR(Fetch, fetch)
- {
- Register &reg = registers[instr->fetch.reg];
-
- if (reg.isUndefined())
- THROW_EXCEPTION(instr->fetch.exceptionId);
-
- QObject *object = reg.getQObject();
- if (!object) {
- THROW_EXCEPTION(instr->fetch.exceptionId);
- } else {
- if (bindingInvalidated(invalidated, object, context, instr->fetch.index))
- goto programExit;
-
- const Register::Type valueType = (Register::Type)instr->fetch.valueType;
- reg.init(valueType);
- if (instr->fetch.valueType >= FirstCleanupType)
- MARK_REGISTER(instr->fetch.reg);
-
- QQmlData::flushPendingBinding(object, instr->fetch.index);
-
- void *argv[] = { reg.typeDataPtr(), 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv);
- if (valueType == FloatType) {
- // promote floats
- const double v = reg.getfloat();
- reg.setnumber(v);
- }
-
- if (instr->fetch.subIndex != static_cast<quint32>(-1))
- subscribe(object, instr->fetch.subIndex, instr->fetch.subOffset, context->engine);
-
- }
- }
- QML_V4_END_INSTR(Fetch, fetch)
-
- QML_V4_BEGIN_INSTR(TestV4Store, storetest)
- {
- Register &data = registers[instr->storetest.reg];
- testBindingResult(*testBindingSource, bindingLine, bindingColumn, context,
- scope, data, instr->storetest.regType);
- }
- QML_V4_END_INSTR(TestV4Store, storetest)
-
- QML_V4_BEGIN_INSTR(Store, store)
- {
- Register &data = registers[instr->store.reg];
-
- if (data.isUndefined())
- THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value"));
-
- if (data.gettype() == QObjectStarType) {
- if (QObject *dataObject = data.getQObject()) {
- QQmlMetaObject dataMo(dataObject);
-
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
-
- QQmlMetaObject receiverMo;
-
- if (QQmlData::get(output, false) && QQmlData::get(output, false)->propertyCache) {
- QQmlPropertyData *receiver =
- QQmlData::get(output, false)->propertyCache->property(instr->store.index);
- receiverMo = ep->rawMetaObjectForType(receiver->propType);
- } else {
- QMetaProperty receiver = output->metaObject()->property(instr->store.index);
- receiverMo = ep->rawMetaObjectForType(receiver.userType());
- }
-
- // Verify that these types are compatible
- if (!QQmlMetaObject::canConvert(dataMo, receiverMo)) {
- THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign ") +
- QLatin1String(dataMo.className()) +
- QLatin1String(" to ") +
- QLatin1String(receiverMo.className()));
- }
- }
- }
-
- if (instr->store.valueType == FloatType) {
- // cast numbers to floats
- const float v = (float) data.getnumber();
- data.setfloat(v);
- }
-
- if (data.gettype() == V8HandleType) {
- // This property must be a VME var property
- QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(output);
- Q_ASSERT(vmemo);
- vmemo->setVMEProperty(instr->store.index, *data.gethandleptr());
- } else {
- int status = -1;
- void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags };
- QMetaObject::metacall(output, QMetaObject::WriteProperty,
- instr->store.index, argv);
- }
-
- goto programExit;
- }
- QML_V4_END_INSTR(Store, store)
-
- QML_V4_BEGIN_INSTR(Copy, copy)
- registers[instr->copy.reg].copy(registers[instr->copy.src]);
- if (registers[instr->copy.reg].gettype() >= FirstCleanupType)
- MARK_REGISTER(instr->copy.reg);
- QML_V4_END_INSTR(Copy, copy)
-
- QML_V4_BEGIN_INSTR(Jump, jump)
- if (instr->jump.reg == -1 || !registers[instr->jump.reg].getbool())
- code += instr->jump.count;
- QML_V4_END_INSTR(Jump, jump)
-
- QML_V4_BEGIN_INSTR(BranchTrue, branchop)
- if (registers[instr->branchop.reg].getbool())
- code += instr->branchop.offset;
- QML_V4_END_INSTR(BranchTrue, branchop)
-
- QML_V4_BEGIN_INSTR(BranchFalse, branchop)
- if (! registers[instr->branchop.reg].getbool())
- code += instr->branchop.offset;
- QML_V4_END_INSTR(BranchFalse, branchop)
-
- QML_V4_BEGIN_INSTR(Branch, branchop)
- code += instr->branchop.offset;
- QML_V4_END_INSTR(Branch, branchop)
-
- QML_V4_BEGIN_INSTR(Block, blockop)
- executedBlocks |= instr->blockop.block;
- QML_V4_END_INSTR(Block, blockop)
-
- QML_V4_BEGIN_INSTR(CleanupRegister, cleanup)
- registers[instr->cleanup.reg].cleanup();
- QML_V4_END_INSTR(CleanupRegister, cleanup)
-
- QML_V4_BEGIN_INSTR(Throw, throwop)
- THROW_VALUE_EXCEPTION_STR(instr->throwop.exceptionId, *registers[instr->throwop.message].getstringptr());
- QML_V4_END_INSTR(Throw, throwop)
-
-#ifdef QML_THREADED_INTERPRETER
- // nothing to do
-#else
- default:
- qFatal("QV4: Unknown instruction %d encountered.", instr->common.type);
- break;
- } // switch
-
- } // while
-#endif
-
- Q_ASSERT(!"Unreachable code reached");
-
-programExit:
-exceptionExit:
- delete testBindingSource;
-
- int reg = 0;
- while (cleanupRegisterMask) {
- if (cleanupRegisterMask & 0x1)
- registers[reg].cleanup();
-
- reg++;
- cleanupRegisterMask >>= 1;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h
deleted file mode 100644
index adb05ba1f4..0000000000
--- a/src/qml/qml/v4/qv4bindings_p.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4BINDINGS_P_H
-#define QV4BINDINGS_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/qqmlexpression_p.h"
-#include "private/qqmlbinding_p.h"
-#include "private/qqmlinstruction_p.h"
-#include "private/qv4instruction_p.h"
-#include "private/qpointervaluepair_p.h"
-
-QT_BEGIN_NAMESPACE
-
-struct QV4Program;
-class QV4Bindings : public QQmlAbstractExpression,
- public QQmlRefCount
-{
- Q_DECLARE_TR_FUNCTIONS(QV4Bindings)
-public:
- QV4Bindings(const char *program, QQmlContextData *context);
- virtual ~QV4Bindings();
-
- QQmlAbstractBinding *configBinding(QObject *target, QObject *scope,
- const QQmlInstruction::instr_assignV4Binding *);
-
-#ifdef QML_THREADED_INTERPRETER
- static void **getDecodeInstrTable();
-#endif
-
- struct Binding : public QQmlAbstractBinding, public QQmlDelayedError {
- Binding()
- : QQmlAbstractBinding(V4), target(0), scope(0), instruction(0), executedBlocks(0), parent(0) {}
-
- // Inherited from QQmlAbstractBinding
- static void destroy(QQmlAbstractBinding *, QQmlAbstractBinding::DestroyMode mode);
- static int propertyIndex(const QQmlAbstractBinding *);
- static QObject *object(const QQmlAbstractBinding *);
- static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags);
- static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags);
- static void retargetBinding(QQmlAbstractBinding *, QObject *, int);
-
- void disconnect();
-
- void dump();
-
- struct Retarget {
- QObject *target;
- int targetProperty;
- };
-
- QPointerValuePair<QObject, Retarget> target;
- QObject *scope;
-
- // To save memory, we store flags inside the instruction pointer.
- // instruction.flag1: enabled
- // instruction.flag2: updating
- QFlagPointer<const QQmlInstruction::instr_assignV4Binding> instruction;
-
- quint32 executedBlocks;
- QV4Bindings *parent;
-
- inline bool enabledFlag() const { return instruction.flag(); }
- inline void setEnabledFlag(bool v) { instruction.setFlagValue(v); }
- inline bool updatingFlag() const { return instruction.flag2(); }
- inline void setUpdatingFlag(bool v) { instruction.setFlag2Value(v); }
- };
-
-private:
- Q_DISABLE_COPY(QV4Bindings)
-
- class Subscription : public QQmlNotifierEndpoint
- {
- public:
- inline Subscription();
-
- // Index of this Subscription into the QV4Bindings::subscriptions array.
- // This may not be used before setBindings() was called.
- inline int method() const;
-
- inline void setBindings(QV4Bindings *bindings);
- inline QV4Bindings *bindings() const;
-
- inline bool active() const;
- inline void setActive(bool active);
-
- // Pointer to the parent QV4Bindings. The flag is used as the 'active' value.
- QFlagPointer<QV4Bindings> m_bindings;
- };
- friend void QV4BindingsSubscription_callback(QQmlNotifierEndpoint *e, void **);
-
- Subscription *subscriptions;
-
- void subscriptionNotify(int);
- void run(Binding *, QQmlPropertyPrivate::WriteFlags flags);
-
- QV4Program *program;
- Binding *bindings;
-
- void init();
- void run(int instr, quint32 &executedBlocks, QQmlContextData *context,
- QQmlDelayedError *error, QObject *scope, QObject *output,
- QQmlPropertyPrivate::WriteFlags storeFlags,
- bool *invalidated
-#ifdef QML_THREADED_INTERPRETER
- , void ***decode_instr = 0
-#endif
- );
-
-
- inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex);
- inline void subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngine *);
-
- inline static qint32 toInt32(double n);
- static const double D32;
- static quint32 toUint32(double n);
-
-};
-
-QT_END_NAMESPACE
-
-#endif // QV4BINDINGS_P_H
-
diff --git a/src/qml/qml/v4/qv4booleanobject.cpp b/src/qml/qml/v4/qv4booleanobject.cpp
new file mode 100644
index 0000000000..24678d23dc
--- /dev/null
+++ b/src/qml/qml/v4/qv4booleanobject.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4booleanobject_p.h"
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(BooleanCtor);
+
+BooleanCtor::BooleanCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier("Boolean"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value BooleanCtor::construct(Managed *m, Value *args, int argc)
+{
+ bool n = argc ? args[0].toBoolean() : false;
+ return Value::fromObject(m->engine()->newBooleanObject(Value::fromBoolean(n)));
+}
+
+Value BooleanCtor::call(Managed *, const Value &, Value *argv, int argc)
+{
+ bool value = argc ? argv[0].toBoolean() : 0;
+ return Value::fromBoolean(value);
+}
+
+void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf);
+}
+
+Value BooleanPrototype::method_toString(SimpleCallContext *ctx)
+{
+ bool result;
+ if (ctx->thisObject.isBoolean()) {
+ result = ctx->thisObject.booleanValue();
+ } else {
+ BooleanObject *thisObject = ctx->thisObject.asBooleanObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+ result = thisObject->value.booleanValue();
+ }
+
+ return Value::fromString(ctx, QLatin1String(result ? "true" : "false"));
+}
+
+Value BooleanPrototype::method_valueOf(SimpleCallContext *ctx)
+{
+ BooleanObject *thisObject = ctx->thisObject.asBooleanObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ return thisObject->value;
+}
diff --git a/src/qml/qml/v4/qv4booleanobject_p.h b/src/qml/qml/v4/qv4booleanobject_p.h
new file mode 100644
index 0000000000..3e5e7663f2
--- /dev/null
+++ b/src/qml/qml/v4/qv4booleanobject_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4BOOLEANOBJECT_H
+#define QBOOLEANOBJECT_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct BooleanCtor: FunctionObject
+{
+ BooleanCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct BooleanPrototype: BooleanObject
+{
+ BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_valueOf(SimpleCallContext *ctx);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4codegen.cpp b/src/qml/qml/v4/qv4codegen.cpp
new file mode 100644
index 0000000000..d0c43c8f56
--- /dev/null
+++ b/src/qml/qml/v4/qv4codegen.cpp
@@ -0,0 +1,2605 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4codegen_p.h"
+#include "qv4util_p.h"
+#include "qv4debugging_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 <qv4runtime_p.h>
+#include <qv4context_p.h>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+#ifdef CONST
+#undef CONST
+#endif
+
+#define QV4_NO_LIVENESS
+#undef SHOW_SSA
+
+using namespace QQmlJS;
+using namespace AST;
+
+class Codegen::ScanFunctions: Visitor
+{
+ typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
+public:
+ ScanFunctions(Codegen *cg, const QString &sourceCode)
+ : _cg(cg)
+ , _sourceCode(sourceCode)
+ , _env(0)
+ , _inFuncBody(false)
+ , _allowFuncDecls(true)
+ {
+ }
+
+ void operator()(Node *node)
+ {
+ if (node)
+ node->accept(this);
+ }
+
+ inline void enterEnvironment(Node *node)
+ {
+ Environment *e = _cg->newEnvironment(node, _env);
+ if (!e->isStrict)
+ e->isStrict = _cg->_strictMode;
+ _envStack.append(e);
+ _env = e;
+ }
+
+ inline void leaveEnvironment()
+ {
+ _envStack.pop();
+ _env = _envStack.isEmpty() ? 0 : _envStack.top();
+ }
+
+protected:
+ using Visitor::visit;
+ using Visitor::endVisit;
+
+ void 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 == QStringLiteral("use strict")) {
+ _env->isStrict = true;
+ } else {
+ // TODO: give a warning.
+ }
+ continue;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ void checkName(const QStringRef &name, const SourceLocation &loc)
+ {
+ if (_env->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, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word"));
+ }
+ }
+ }
+ void checkForArguments(AST::FormalParameterList *parameters)
+ {
+ while (parameters) {
+ if (parameters->name == QStringLiteral("arguments"))
+ _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
+ parameters = parameters->next;
+ }
+ }
+
+ virtual bool visit(Program *ast)
+ {
+ enterEnvironment(ast);
+ checkDirectivePrologue(ast->elements);
+ return true;
+ }
+
+ virtual void endVisit(Program *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(CallExpression *ast)
+ {
+ if (! _env->hasDirectEval) {
+ if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
+ if (id->name == QStringLiteral("eval")) {
+ if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown)
+ _env->usesArgumentsObject = Environment::ArgumentsObjectUsed;
+ _env->hasDirectEval = true;
+ }
+ }
+ }
+ int argc = 0;
+ for (ArgumentList *it = ast->arguments; it; it = it->next)
+ ++argc;
+ _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc);
+ return true;
+ }
+
+ virtual bool visit(NewMemberExpression *ast)
+ {
+ int argc = 0;
+ for (ArgumentList *it = ast->arguments; it; it = it->next)
+ ++argc;
+ _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc);
+ return true;
+ }
+
+ virtual bool 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;
+ }
+ _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index);
+ return true;
+ }
+
+ virtual bool visit(VariableDeclaration *ast)
+ {
+ if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode"));
+ checkName(ast->name, ast->identifierToken);
+ if (ast->name == QLatin1String("arguments"))
+ _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
+ _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration);
+ return true;
+ }
+
+ virtual bool visit(IdentifierExpression *ast)
+ {
+ checkName(ast->name, ast->identifierToken);
+ if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
+ _env->usesArgumentsObject = Environment::ArgumentsObjectUsed;
+ return true;
+ }
+
+ virtual bool visit(ExpressionStatement *ast)
+ {
+ if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
+ if (!_allowFuncDecls)
+ _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "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) == QStringLiteral("function")) {
+ _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token"));
+ }
+ }
+ return true;
+ }
+
+ virtual bool visit(FunctionExpression *ast)
+ {
+ enterFunction(ast, /*enterName*/ false);
+ return true;
+ }
+
+ void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true)
+ {
+ if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode"));
+ enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression);
+ }
+
+ virtual void endVisit(FunctionExpression *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(ObjectLiteral *ast)
+ {
+ int argc = 0;
+ for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
+ ++argc;
+ if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
+ ++argc;
+ }
+ _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
+ }
+
+ virtual bool visit(PropertyGetterSetter *ast)
+ {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false);
+ return true;
+ }
+
+ virtual void endVisit(PropertyGetterSetter *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(FunctionDeclaration *ast)
+ {
+ enterFunction(ast, /*enterName*/ true, /*isExpression */false);
+ return true;
+ }
+
+ virtual void endVisit(FunctionDeclaration *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(FunctionBody *ast)
+ {
+ TemporaryBoolAssignment inFuncBody(_inFuncBody, true);
+ Node::accept(ast->elements, this);
+ return false;
+ }
+
+ virtual bool visit(WithStatement *ast)
+ {
+ if (_env->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode"));
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual bool visit(IfStatement *ast) {
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody);
+ Node::accept(ast->ok, this);
+ Node::accept(ast->ko, this);
+
+ return false;
+ }
+
+ virtual bool visit(WhileStatement *ast) {
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody);
+ Node::accept(ast->statement, this);
+
+ return false;
+ }
+
+ virtual bool visit(DoWhileStatement *ast) {
+ {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict);
+ Node::accept(ast->statement, this);
+ }
+ Node::accept(ast->expression, this);
+ return false;
+ }
+
+ virtual bool visit(ForStatement *ast) {
+ Node::accept(ast->initialiser, this);
+ Node::accept(ast->condition, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+ }
+
+ virtual bool visit(LocalForStatement *ast) {
+ Node::accept(ast->declarations, this);
+ Node::accept(ast->condition, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+ }
+
+ virtual bool visit(ForEachStatement *ast) {
+ Node::accept(ast->initialiser, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+ }
+
+ virtual bool visit(LocalForEachStatement *ast) {
+ Node::accept(ast->declaration, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+ }
+
+ virtual bool visit(Block *ast) {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls);
+ Node::accept(ast->statements, this);
+ return false;
+ }
+
+private:
+ void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression)
+ {
+ bool wasStrict = false;
+ if (_env) {
+ _env->hasNestedFunctions = true;
+ // The identifier of a function expression cannot be referenced from the enclosing environment.
+ if (expr)
+ _env->enter(name, Environment::FunctionDefinition, expr);
+ if (name == QLatin1String("arguments"))
+ _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
+ wasStrict = _env->isStrict;
+ }
+
+ enterEnvironment(ast);
+ checkForArguments(formals);
+
+ _env->isNamedFunctionExpression = isExpression && !name.isEmpty();
+ _env->formals = formals;
+
+ if (body)
+ checkDirectivePrologue(body->elements);
+
+ if (wasStrict || _env->isStrict) {
+ QStringList args;
+ for (FormalParameterList *it = formals; it; it = it->next) {
+ QString arg = it->name.toString();
+ if (args.contains(arg))
+ _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
+ if (arg == QLatin1String("eval") || arg == QLatin1String("arguments"))
+ _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg));
+ args += arg;
+ }
+ }
+ }
+
+private: // fields:
+ Codegen *_cg;
+ const QString _sourceCode;
+ Environment *_env;
+ QStack<Environment *> _envStack;
+
+ bool _inFuncBody;
+ bool _allowFuncDecls;
+};
+
+Codegen::Codegen(QV4::ExecutionContext *context, bool strict)
+ : _module(0)
+ , _function(0)
+ , _block(0)
+ , _exitBlock(0)
+ , _throwBlock(0)
+ , _returnAddress(0)
+ , _mode(GlobalCode)
+ , _env(0)
+ , _loop(0)
+ , _labelledStatement(0)
+ , _scopeAndFinally(0)
+ , _context(context)
+ , _strictMode(strict)
+ , _errorHandler(0)
+{
+}
+
+Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode)
+ : _module(0)
+ , _function(0)
+ , _block(0)
+ , _exitBlock(0)
+ , _throwBlock(0)
+ , _returnAddress(0)
+ , _mode(GlobalCode)
+ , _env(0)
+ , _loop(0)
+ , _labelledStatement(0)
+ , _scopeAndFinally(0)
+ , _context(0)
+ , _strictMode(strictMode)
+ , _errorHandler(errorHandler)
+{
+}
+
+V4IR::Function *Codegen::operator()(const QString &fileName,
+ const QString &sourceCode,
+ Program *node,
+ V4IR::Module *module,
+ Mode mode,
+ const QStringList &inheritedLocals)
+{
+ assert(node);
+
+ _fileName = fileName;
+ _module = module;
+ _env = 0;
+
+ ScanFunctions scan(this, sourceCode);
+ scan(node);
+
+ V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0,
+ node->elements, mode, inheritedLocals);
+ qDeleteAll(_envMap);
+ _envMap.clear();
+
+ return globalCode;
+}
+
+V4IR::Function *Codegen::operator()(const QString &fileName,
+ const QString &sourceCode,
+ AST::FunctionExpression *ast,
+ V4IR::Module *module)
+{
+ _fileName = fileName;
+ _module = module;
+ _env = 0;
+
+ ScanFunctions scan(this, sourceCode);
+ // fake a global environment
+ scan.enterEnvironment(0);
+ scan(ast);
+ scan.leaveEnvironment();
+
+ V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
+
+ qDeleteAll(_envMap);
+ _envMap.clear();
+
+ return function;
+}
+
+
+void Codegen::enterEnvironment(Node *node)
+{
+ _env = _envMap.value(node);
+ assert(_env);
+}
+
+void Codegen::leaveEnvironment()
+{
+ assert(_env);
+ _env = _env->parent;
+}
+
+void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock)
+{
+ if (startBlock)
+ startBlock->markAsGroupStart();
+ _loop = new Loop(node, startBlock, 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;
+}
+
+V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name)
+{
+ if (base->asTemp() /*|| base->asName()*/)
+ return _block->MEMBER(base->asTemp(), name);
+ else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), base);
+ return _block->MEMBER(_block->TEMP(t), name);
+ }
+}
+
+V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index)
+{
+ if (! base->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), base);
+ base = _block->TEMP(t);
+ }
+
+ if (! index->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), index);
+ index = _block->TEMP(t);
+ }
+
+ assert(base->asTemp() && index->asTemp());
+ return _block->SUBSCRIPT(base->asTemp(), index->asTemp());
+}
+
+V4IR::Expr *Codegen::argument(V4IR::Expr *expr)
+{
+ if (expr && ! expr->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), expr);
+ expr = _block->TEMP(t);
+ }
+ return expr;
+}
+
+// keeps references alive, converts other expressions to temps
+V4IR::Expr *Codegen::reference(V4IR::Expr *expr)
+{
+ if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), expr);
+ expr = _block->TEMP(t);
+ }
+ return expr;
+}
+
+V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr)
+{
+ Q_ASSERT(op != V4IR::OpIncrement);
+ Q_ASSERT(op != V4IR::OpDecrement);
+
+ if (V4IR::Const *c = expr->asConst()) {
+ if (c->type == V4IR::NumberType) {
+ switch (op) {
+ case V4IR::OpNot:
+ return _block->CONST(V4IR::BoolType, !c->value);
+ case V4IR::OpUMinus:
+ return _block->CONST(V4IR::NumberType, -c->value);
+ case V4IR::OpUPlus:
+ return expr;
+ case V4IR::OpCompl:
+ return _block->CONST(V4IR::NumberType, ~QV4::Value::toInt32(c->value));
+ case V4IR::OpIncrement:
+ return _block->CONST(V4IR::NumberType, c->value + 1);
+ case V4IR::OpDecrement:
+ return _block->CONST(V4IR::NumberType, c->value - 1);
+ default:
+ break;
+ }
+ }
+ }
+ if (! expr->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), expr);
+ expr = _block->TEMP(t);
+ }
+ assert(expr->asTemp());
+ return _block->UNOP(op, expr->asTemp());
+}
+
+V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right)
+{
+ if (V4IR::Const *c1 = left->asConst()) {
+ if (V4IR::Const *c2 = right->asConst()) {
+ if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) {
+ switch (op) {
+ case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value);
+ case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0);
+ case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value));
+ case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value));
+ case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value));
+ case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value);
+ case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value);
+ case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value);
+ case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value);
+ case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value);
+ case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value);
+ case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value);
+ case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value);
+ case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value);
+ case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) << (QV4::Value::toUInt32(c2->value) & 0x1f));
+ case V4IR::OpMod: return _block->CONST(V4IR::NumberType, std::fmod(c1->value, c2->value));
+ case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value);
+ case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value);
+ case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f));
+ case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value);
+ case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,QV4::Value::toUInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f));
+
+ case V4IR::OpInstanceof:
+ case V4IR::OpIn:
+ break;
+
+ case V4IR::OpIfTrue: // unary ops
+ case V4IR::OpNot:
+ case V4IR::OpUMinus:
+ case V4IR::OpUPlus:
+ case V4IR::OpCompl:
+ case V4IR::OpIncrement:
+ case V4IR::OpDecrement:
+ case V4IR::OpInvalid:
+ break;
+ }
+ }
+ }
+ } else if (op == V4IR::OpAdd) {
+ if (V4IR::String *s1 = left->asString()) {
+ if (V4IR::String *s2 = right->asString()) {
+ return _block->STRING(_function->newString(*s1->value + *s2->value));
+ }
+ }
+ }
+
+ if (!left->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), left);
+ left = _block->TEMP(t);
+ }
+
+ if (!right->asTemp()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), right);
+ right = _block->TEMP(t);
+ }
+
+ assert(left->asTemp());
+ assert(right->asTemp());
+
+ return _block->BINOP(op, left, right);
+}
+
+V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args)
+{
+ base = reference(base);
+ return _block->CALL(base, args);
+}
+
+void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op)
+{
+ assert(target->isLValue());
+
+ // TODO: verify the rest of the function for when op == OpInvalid
+ if (op != V4IR::OpInvalid) {
+ move(target, binop(op, target, source), V4IR::OpInvalid);
+ return;
+ }
+
+ if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) {
+ unsigned t = _block->newTemp();
+ _block->MOVE(_block->TEMP(t), source);
+ source = _block->TEMP(t);
+ }
+ if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) {
+ unsigned t = _block->newTemp();
+ _block->MOVE(_block->TEMP(t), source);
+ source = _block->TEMP(t);
+ }
+
+ _block->MOVE(target, source, op);
+}
+
+void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse)
+{
+ if (! (cond->asTemp() || cond->asBinop())) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), cond);
+ cond = _block->TEMP(t);
+ }
+ _block->CJUMP(cond, iftrue, iffalse);
+}
+
+void Codegen::accept(Node *node)
+{
+ if (node)
+ node->accept(this);
+}
+
+void Codegen::statement(Statement *ast)
+{
+ _block->nextLocation = ast->firstSourceLocation();
+ accept(ast);
+}
+
+void Codegen::statement(ExpressionNode *ast)
+{
+ if (! ast) {
+ return;
+ } else {
+ Result r(nx);
+ qSwap(_expr, r);
+ accept(ast);
+ 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()) {
+ // there is nothing to do
+ } else {
+ unsigned t = _block->newTemp();
+ move(_block->TEMP(t), *r);
+ }
+ }
+ }
+}
+
+void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse)
+{
+ if (ast) {
+ Result r(iftrue, iffalse);
+ qSwap(_expr, r);
+ accept(ast);
+ qSwap(_expr, r);
+ if (r.format == ex) {
+ cjump(*r, r.iftrue, r.iffalse);
+ }
+ }
+}
+
+Codegen::Result Codegen::expression(ExpressionNode *ast)
+{
+ Result r;
+ if (ast) {
+ qSwap(_expr, r);
+ accept(ast);
+ qSwap(_expr, r);
+ }
+ return r;
+}
+
+QString Codegen::propertyName(PropertyName *ast)
+{
+ QString p;
+ if (ast) {
+ qSwap(_property, p);
+ accept(ast);
+ qSwap(_property, p);
+ }
+ return p;
+}
+
+Codegen::Result Codegen::sourceElement(SourceElement *ast)
+{
+ Result r(nx);
+ if (ast) {
+ qSwap(_expr, r);
+ accept(ast);
+ qSwap(_expr, r);
+ }
+ 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)
+ sourceElements(ast->elements);
+}
+
+void Codegen::program(Program *ast)
+{
+ if (ast) {
+ sourceElements(ast->elements);
+ }
+}
+
+void Codegen::sourceElements(SourceElements *ast)
+{
+ for (SourceElements *it = ast; it; it = it->next) {
+ sourceElement(it->element);
+ }
+}
+
+void Codegen::variableDeclaration(VariableDeclaration *ast)
+{
+ V4IR::Expr *initializer = 0;
+ if (!ast->expression)
+ return;
+ Result expr = expression(ast->expression);
+ assert(expr.code);
+ initializer = *expr;
+
+ if (! _env->parent || _function->insideWithOrCatch) {
+ // it's global code.
+ move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer);
+ } else {
+ const int index = _env->findMember(ast->name.toString());
+ assert(index != -1);
+ move(_block->LOCAL(index, 0), initializer);
+ }
+}
+
+void Codegen::variableDeclarationList(VariableDeclarationList *ast)
+{
+ for (VariableDeclarationList *it = ast; it; it = it->next) {
+ variableDeclaration(it->declaration);
+ }
+}
+
+
+bool Codegen::visit(ArgumentList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(CaseBlock *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(CaseClause *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(CaseClauses *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(Catch *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(DefaultClause *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(ElementList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(Elision *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(Finally *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(FormalParameterList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(FunctionBody *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(Program *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(PropertyAssignmentList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(PropertyNameAndValue *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(PropertyGetterSetter *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(SourceElements *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(StatementList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiArrayMemberList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiImport *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiImportList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiObjectInitializer *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiObjectMemberList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiParameterList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiProgram *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(UiQualifiedId *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(VariableDeclaration *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(VariableDeclarationList *)
+{
+ assert(!"unreachable");
+ return false;
+}
+
+bool Codegen::visit(Expression *ast)
+{
+ statement(ast->left);
+ accept(ast->right);
+ return false;
+}
+
+bool Codegen::visit(ArrayLiteral *ast)
+{
+ V4IR::ExprList *args = 0;
+ V4IR::ExprList *current = 0;
+ for (ElementList *it = ast->elements; it; it = it->next) {
+ for (Elision *elision = it->elision; elision; elision = elision->next) {
+ V4IR::ExprList *arg = _function->New<V4IR::ExprList>();
+ if (!current) {
+ args = arg;
+ } else {
+ current->next = arg;
+ }
+ current = arg;
+ current->expr = _block->CONST(V4IR::MissingType, 0);
+ }
+ Result expr = expression(it->expression);
+
+ V4IR::ExprList *arg = _function->New<V4IR::ExprList>();
+ if (!current) {
+ args = arg;
+ } else {
+ current->next = arg;
+ }
+ current = arg;
+
+ V4IR::Expr *exp = *expr;
+ if (exp->asTemp() || exp->asConst()) {
+ current->expr = exp;
+ } else {
+ unsigned value = _block->newTemp();
+ move(_block->TEMP(value), exp);
+ current->expr = _block->TEMP(value);
+ }
+ }
+ for (Elision *elision = ast->elision; elision; elision = elision->next) {
+ V4IR::ExprList *arg = _function->New<V4IR::ExprList>();
+ if (!current) {
+ args = arg;
+ } else {
+ current->next = arg;
+ }
+ current = arg;
+ current->expr = _block->CONST(V4IR::MissingType, 0);
+ }
+
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(ArrayMemberExpression *ast)
+{
+ Result base = expression(ast->base);
+ Result index = expression(ast->expression);
+ _expr.code = subscript(*base, *index);
+ return false;
+}
+
+static V4IR::AluOp baseOp(int op)
+{
+ switch ((QSOperator::Op) op) {
+ case QSOperator::InplaceAnd: return V4IR::OpBitAnd;
+ case QSOperator::InplaceSub: return V4IR::OpSub;
+ case QSOperator::InplaceDiv: return V4IR::OpDiv;
+ case QSOperator::InplaceAdd: return V4IR::OpAdd;
+ case QSOperator::InplaceLeftShift: return V4IR::OpLShift;
+ case QSOperator::InplaceMod: return V4IR::OpMod;
+ case QSOperator::InplaceMul: return V4IR::OpMul;
+ case QSOperator::InplaceOr: return V4IR::OpBitOr;
+ case QSOperator::InplaceRightShift: return V4IR::OpRShift;
+ case QSOperator::InplaceURightShift: return V4IR::OpURShift;
+ case QSOperator::InplaceXor: return V4IR::OpBitXor;
+ default: return V4IR::OpInvalid;
+ }
+}
+
+bool Codegen::visit(BinaryExpression *ast)
+{
+ if (ast->op == QSOperator::And) {
+ if (_expr.accept(cx)) {
+ V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock());
+ condition(ast->left, iftrue, _expr.iffalse);
+ _block = iftrue;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock());
+
+ const unsigned r = _block->newTemp();
+
+ move(_block->TEMP(r), *expression(ast->left));
+ cjump(_block->TEMP(r), iftrue, endif);
+ _block = iftrue;
+ move(_block->TEMP(r), *expression(ast->right));
+ _block->JUMP(endif);
+
+ _expr.code = _block->TEMP(r);
+ _block = endif;
+ }
+ return false;
+ } else if (ast->op == QSOperator::Or) {
+ if (_expr.accept(cx)) {
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock());
+ condition(ast->left, _expr.iftrue, iffalse);
+ _block = iffalse;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock());
+
+ const unsigned r = _block->newTemp();
+ move(_block->TEMP(r), *expression(ast->left));
+ cjump(_block->TEMP(r), endif, iffalse);
+ _block = iffalse;
+ move(_block->TEMP(r), *expression(ast->right));
+ _block->JUMP(endif);
+
+ _block = endif;
+ _expr.code = _block->TEMP(r);
+ }
+ return false;
+ }
+
+ V4IR::Expr* left = *expression(ast->left);
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation());
+
+ switch (ast->op) {
+ case QSOperator::Or:
+ case QSOperator::And:
+ break;
+
+ case QSOperator::Assign: {
+ V4IR::Expr* right = *expression(ast->right);
+ if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember()))
+ throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue"));
+
+ 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);
+ }
+ break;
+ }
+
+ 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: {
+ V4IR::Expr* right = *expression(ast->right);
+ if (!left->isLValue())
+ throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue"));
+
+ if (_expr.accept(nx)) {
+ move(left, right, baseOp(ast->op));
+ } else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), right);
+ move(left, _block->TEMP(t), baseOp(ast->op));
+ _expr.code = left;
+ }
+ break;
+ }
+
+ case QSOperator::In:
+ case QSOperator::InstanceOf:
+ case QSOperator::Equal:
+ case QSOperator::NotEqual:
+ case QSOperator::Ge:
+ case QSOperator::Gt:
+ case QSOperator::Le:
+ case QSOperator::Lt:
+ case QSOperator::StrictEqual:
+ case QSOperator::StrictNotEqual: {
+ if (!left->asTemp() && !left->asConst()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), left);
+ left = _block->TEMP(t);
+ }
+
+ V4IR::Expr* right = *expression(ast->right);
+
+ if (_expr.accept(cx)) {
+ cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse);
+ } else {
+ V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right);
+ if (e->asConst() || e->asString())
+ _expr.code = e;
+ else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), e);
+ _expr.code = _block->TEMP(t);
+ }
+ }
+ break;
+ }
+
+ case QSOperator::Add:
+ case QSOperator::BitAnd:
+ case QSOperator::BitOr:
+ case QSOperator::BitXor:
+ case QSOperator::Div:
+ case QSOperator::LShift:
+ case QSOperator::Mod:
+ case QSOperator::Mul:
+ case QSOperator::RShift:
+ case QSOperator::Sub:
+ case QSOperator::URShift: {
+ if (!left->asTemp() && !left->asConst()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), left);
+ left = _block->TEMP(t);
+ }
+
+ V4IR::Expr* right = *expression(ast->right);
+
+ V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right);
+ if (e->asConst() || e->asString())
+ _expr.code = e;
+ else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), e);
+ _expr.code = _block->TEMP(t);
+ }
+ break;
+ }
+
+ } // switch
+
+ return false;
+}
+
+bool Codegen::visit(CallExpression *ast)
+{
+ Result base = expression(ast->base);
+ V4IR::ExprList *args = 0, **args_it = &args;
+ for (ArgumentList *it = ast->arguments; it; it = it->next) {
+ Result arg = expression(it->expression);
+ V4IR::Expr *actual = argument(*arg);
+ *args_it = _function->New<V4IR::ExprList>();
+ (*args_it)->init(actual);
+ args_it = &(*args_it)->next;
+ }
+ _expr.code = call(*base, args);
+ return false;
+}
+
+bool Codegen::visit(ConditionalExpression *ast)
+{
+ V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock());
+
+ const unsigned t = _block->newTemp();
+
+ condition(ast->expression, iftrue, iffalse);
+
+ _block = iftrue;
+ move(_block->TEMP(t), *expression(ast->ok));
+ _block->JUMP(endif);
+
+ _block = iffalse;
+ move(_block->TEMP(t), *expression(ast->ko));
+ _block->JUMP(endif);
+
+ _block = endif;
+
+ _expr.code = _block->TEMP(t);
+
+ return false;
+}
+
+bool Codegen::visit(DeleteExpression *ast)
+{
+ V4IR::Expr* expr = *expression(ast->expression);
+ // Temporaries cannot be deleted
+ if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) {
+ // Trying to delete a function argument might throw.
+ if (_function->isStrict && expr->asTemp()->index < 0)
+ throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode.");
+ _expr.code = _block->CONST(V4IR::BoolType, 0);
+ return false;
+ }
+ if (_function->isStrict && expr->asName())
+ throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode.");
+
+ // [[11.4.1]] Return true if it's not a reference
+ if (expr->asConst() || expr->asString()) {
+ _expr.code = _block->CONST(V4IR::BoolType, 1);
+ 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(V4IR::BoolType, 1);
+ return false;
+ }
+ if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) {
+ _expr.code = _block->CONST(V4IR::BoolType, 1);
+ return false;
+ }
+
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(reference(expr));
+ _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args);
+ return false;
+}
+
+bool Codegen::visit(FalseLiteral *)
+{
+ if (_expr.accept(cx)) {
+ _block->JUMP(_expr.iffalse);
+ } else {
+ _expr.code = _block->CONST(V4IR::BoolType, 0);
+ }
+ return false;
+}
+
+bool Codegen::visit(FieldMemberExpression *ast)
+{
+ Result base = expression(ast->base);
+ _expr.code = member(*base, _function->newString(ast->name.toString()));
+ return false;
+}
+
+bool Codegen::visit(FunctionExpression *ast)
+{
+ V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
+ _expr.code = _block->CLOSURE(function);
+ return false;
+}
+
+V4IR::Expr *Codegen::identifier(const QString &name, int line, int col)
+{
+ uint scope = 0;
+ Environment *e = _env;
+ V4IR::Function *f = _function;
+
+ while (f && e->parent) {
+ if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name))
+ break;
+ int index = e->findMember(name);
+ assert (index < e->members.size());
+ if (index != -1) {
+ return _block->LOCAL(index, scope);
+ }
+ const int argIdx = f->indexOfArgument(&name);
+ if (argIdx != -1)
+ return _block->ARG(argIdx, scope);
+ ++scope;
+ e = e->parent;
+ f = f->outer;
+ }
+
+ if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && _mode != QmlBinding && (!f || f->name != name))
+ return _block->GLOBALNAME(name, line, col);
+
+ // global context or with. Lookup by name
+ return _block->NAME(name, line, col);
+
+}
+
+bool Codegen::visit(IdentifierExpression *ast)
+{
+ _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn);
+ return false;
+}
+
+bool Codegen::visit(NestedExpression *ast)
+{
+ accept(ast->expression);
+ return false;
+}
+
+bool Codegen::visit(NewExpression *ast)
+{
+ Result base = expression(ast->expression);
+ V4IR::Expr *expr = *base;
+ if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), expr);
+ expr = _block->TEMP(t);
+ }
+ _expr.code = _block->NEW(expr, 0);
+ return false;
+}
+
+bool Codegen::visit(NewMemberExpression *ast)
+{
+ Result base = expression(ast->base);
+ V4IR::Expr *expr = *base;
+ if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), expr);
+ expr = _block->TEMP(t);
+ }
+
+ V4IR::ExprList *args = 0, **args_it = &args;
+ for (ArgumentList *it = ast->arguments; it; it = it->next) {
+ Result arg = expression(it->expression);
+ V4IR::Expr *actual = argument(*arg);
+ *args_it = _function->New<V4IR::ExprList>();
+ (*args_it)->init(actual);
+ args_it = &(*args_it)->next;
+ }
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), _block->NEW(expr, args));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(NotExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ const unsigned r = _block->newTemp();
+ move(_block->TEMP(r), unop(V4IR::OpNot, *expr));
+ _expr.code = _block->TEMP(r);
+ return false;
+}
+
+bool Codegen::visit(NullExpression *)
+{
+ if (_expr.accept(cx)) _block->JUMP(_expr.iffalse);
+ else _expr.code = _block->CONST(V4IR::NullType, 0);
+
+ return false;
+}
+
+bool Codegen::visit(NumericLiteral *ast)
+{
+ if (_expr.accept(cx)) {
+ if (ast->value) _block->JUMP(_expr.iftrue);
+ else _block->JUMP(_expr.iffalse);
+ } else {
+ _expr.code = _block->CONST(V4IR::NumberType, ast->value);
+ }
+ return false;
+}
+
+struct ObjectPropertyValue {
+ V4IR::Expr *value;
+ V4IR::Function *getter;
+ V4IR::Function *setter;
+};
+
+bool Codegen::visit(ObjectLiteral *ast)
+{
+ QMap<QString, ObjectPropertyValue> valueMap;
+
+ for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
+ if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
+ QString name = propertyName(nv->name);
+ Result value = expression(nv->value);
+ ObjectPropertyValue &v = valueMap[name];
+ if (v.getter || v.setter || (_function->isStrict && v.value))
+ throwSyntaxError(nv->lastSourceLocation(),
+ QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name));
+
+ valueMap[name].value = *value;
+ } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
+ QString name = propertyName(gs->name);
+ V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0);
+ ObjectPropertyValue &v = valueMap[name];
+ if (v.value ||
+ (gs->type == PropertyGetterSetter::Getter && v.getter) ||
+ (gs->type == PropertyGetterSetter::Setter && v.setter))
+ throwSyntaxError(gs->lastSourceLocation(),
+ QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name));
+ if (gs->type == PropertyGetterSetter::Getter)
+ v.getter = function;
+ else
+ v.setter = function;
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ V4IR::ExprList *args = 0;
+
+ if (!valueMap.isEmpty()) {
+ V4IR::ExprList *current;
+ for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) {
+ if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) {
+ ++it;
+ continue;
+ }
+
+ if (!args) {
+ args = _function->New<V4IR::ExprList>();
+ current = args;
+ } else {
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ }
+
+ current->expr = _block->NAME(it.key(), 0, 0);
+
+ if (it->value) {
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->CONST(V4IR::BoolType, true);
+
+ unsigned value = _block->newTemp();
+ move(_block->TEMP(value), it->value);
+
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->TEMP(value);
+ } else {
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->CONST(V4IR::BoolType, false);
+
+ unsigned getter = _block->newTemp();
+ unsigned setter = _block->newTemp();
+ move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0));
+ move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0));
+
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->TEMP(getter);
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->TEMP(setter);
+ }
+
+ it = valueMap.erase(it);
+ }
+ }
+
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal,
+ ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args));
+
+ // What's left are array entries
+ if (!valueMap.isEmpty()) {
+ unsigned value = 0;
+ unsigned getter = 0;
+ unsigned setter = 0;
+ for (QMap<QString, ObjectPropertyValue>::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) {
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ V4IR::ExprList *current = args;
+ current->expr = _block->TEMP(t);
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->NAME(it.key(), 0, 0);
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+
+ if (it->value) {
+ if (!value)
+ value = _block->newTemp();
+ move(_block->TEMP(value), it->value);
+ // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx)
+ current->expr = _block->TEMP(value);
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args));
+ } else {
+ if (!getter) {
+ getter = _block->newTemp();
+ setter = _block->newTemp();
+ }
+ move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0));
+ move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0));
+
+
+ // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx);
+ current->expr = _block->TEMP(getter);
+ current->next = _function->New<V4IR::ExprList>();
+ current = current->next;
+ current->expr = _block->TEMP(setter);
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args));
+ }
+ }
+ }
+
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(PostDecrementExpression *ast)
+{
+ Result expr = expression(ast->base);
+ if (!expr->isLValue())
+ throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation");
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken);
+
+ if (_expr.accept(nx)) {
+ move(*expr, binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1)));
+ } else {
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(*expr);
+ _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args);
+ }
+ return false;
+}
+
+bool Codegen::visit(PostIncrementExpression *ast)
+{
+ Result expr = expression(ast->base);
+ if (!expr->isLValue())
+ throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation");
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken);
+
+ if (_expr.accept(nx)) {
+ move(*expr, binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1)));
+ } else {
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(*expr);
+ _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args);
+ }
+ return false;
+}
+
+bool Codegen::visit(PreDecrementExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken);
+ V4IR::Expr *op = binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1));
+ if (_expr.accept(nx)) {
+ move(*expr, op);
+ } else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), op);
+ move(*expr, _block->TEMP(t));
+ _expr.code = _block->TEMP(t);
+ }
+ return false;
+}
+
+bool Codegen::visit(PreIncrementExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken);
+ V4IR::Expr *op = binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1));
+ if (_expr.accept(nx)) {
+ move(*expr, op);
+ } else {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), op);
+ move(*expr, _block->TEMP(t));
+ _expr.code = _block->TEMP(t);
+ }
+ return false;
+}
+
+bool Codegen::visit(RegExpLiteral *ast)
+{
+ _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags);
+ return false;
+}
+
+bool Codegen::visit(StringLiteral *ast)
+{
+ _expr.code = _block->STRING(_function->newString(ast->value.toString()));
+ return false;
+}
+
+bool Codegen::visit(ThisExpression *ast)
+{
+ _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn);
+ return false;
+}
+
+bool Codegen::visit(TildeExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), unop(V4IR::OpCompl, *expr));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(TrueLiteral *)
+{
+ if (_expr.accept(cx)) {
+ _block->JUMP(_expr.iftrue);
+ } else {
+ _expr.code = _block->CONST(V4IR::BoolType, 1);
+ }
+ return false;
+}
+
+bool Codegen::visit(TypeOfExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(reference(*expr));
+ _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+ return false;
+}
+
+bool Codegen::visit(UnaryMinusExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(UnaryPlusExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(VoidExpression *ast)
+{
+ statement(ast->expression);
+ _expr.code = _block->CONST(V4IR::UndefinedType, 0);
+ return false;
+}
+
+bool Codegen::visit(FunctionDeclaration * ast)
+{
+ if (_mode == QmlBinding)
+ move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0));
+ _expr.accept(nx);
+ return false;
+}
+
+V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::SourceElements *body, Mode mode,
+ const QStringList &inheritedLocals)
+{
+ qSwap(_mode, mode); // enter function code.
+ Loop *loop = 0;
+ qSwap(_loop, loop);
+
+ ScopeAndFinally *scopeAndFinally = 0;
+
+ enterEnvironment(ast);
+ V4IR::Function *function = _module->newFunction(name, _function);
+ function->sourceFile = _fileName;
+
+ V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock);
+ V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock());
+ function->hasDirectEval = _env->hasDirectEval;
+ function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed);
+ function->maxNumberOfArguments = _env->maxNumberOfArguments;
+ function->isStrict = _env->isStrict;
+ function->isNamedExpression = _env->isNamedFunctionExpression;
+
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ if (_mode == FunctionCode) {
+ unsigned t = 0;
+ for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) {
+ const QString &local = it.key();
+ function->LOCAL(local);
+ (*it).index = t;
+ entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(V4IR::UndefinedType, 0));
+ ++t;
+ }
+ } else {
+ if (!_env->isStrict) {
+ foreach (const QString &inheritedLocal, inheritedLocals) {
+ function->LOCAL(inheritedLocal);
+ unsigned tempIndex = entryBlock->newTemp();
+ Environment::Member member = { Environment::UndefinedMember,
+ static_cast<int>(tempIndex), 0 };
+ _env->members.insert(inheritedLocal, member);
+ }
+ }
+
+ V4IR::ExprList *args = 0;
+ for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) {
+ const QString &local = it.key();
+ V4IR::ExprList *next = function->New<V4IR::ExprList>();
+ next->expr = entryBlock->NAME(local, 0, 0);
+ next->next = args;
+ args = next;
+ }
+ if (args) {
+ V4IR::ExprList *next = function->New<V4IR::ExprList>();
+ next->expr = entryBlock->CONST(V4IR::BoolType, (mode == EvalCode || mode == QmlBinding));
+ next->next = args;
+ args = next;
+
+ entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args));
+ }
+ }
+
+ unsigned returnAddress = entryBlock->newTemp();
+
+ entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0));
+ exitBlock->RET(exitBlock->TEMP(returnAddress));
+ V4IR::ExprList *throwArgs = function->New<V4IR::ExprList>();
+ throwArgs->expr = throwBlock->TEMP(returnAddress);
+ throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
+ throwBlock->JUMP(exitBlock);
+
+ qSwap(_function, function);
+ qSwap(_block, entryBlock);
+ qSwap(_exitBlock, exitBlock);
+ qSwap(_throwBlock, throwBlock);
+ qSwap(_returnAddress, returnAddress);
+ qSwap(_scopeAndFinally, scopeAndFinally);
+
+ for (FormalParameterList *it = formals; it; it = it->next) {
+ _function->RECEIVE(it->name.toString());
+ }
+
+ foreach (const Environment::Member &member, _env->members) {
+ if (member.function) {
+ V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
+ member.function->body ? member.function->body->elements : 0);
+ if (! _env->parent) {
+ move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
+ _block->CLOSURE(function));
+ } else {
+ assert(member.index >= 0);
+ move(_block->LOCAL(member.index, 0), _block->CLOSURE(function));
+ }
+ }
+ }
+
+ sourceElements(body);
+
+ _function->insertBasicBlock(_exitBlock);
+
+ _block->JUMP(_exitBlock);
+
+ qSwap(_function, function);
+ qSwap(_block, entryBlock);
+ qSwap(_exitBlock, exitBlock);
+ qSwap(_throwBlock, throwBlock);
+ qSwap(_returnAddress, returnAddress);
+ qSwap(_scopeAndFinally, scopeAndFinally);
+ qSwap(_loop, loop);
+
+ leaveEnvironment();
+
+ qSwap(_mode, mode);
+
+ return function;
+}
+
+bool Codegen::visit(IdentifierPropertyName *ast)
+{
+ _property = ast->id.toString();
+ return false;
+}
+
+bool Codegen::visit(NumericLiteralPropertyName *ast)
+{
+ _property = QString::number(ast->id, 'g', 16);
+ return false;
+}
+
+bool Codegen::visit(StringLiteralPropertyName *ast)
+{
+ _property = ast->id.toString();
+ return false;
+}
+
+bool Codegen::visit(FunctionSourceElement *ast)
+{
+ statement(ast->declaration);
+ return false;
+}
+
+bool Codegen::visit(StatementSourceElement *ast)
+{
+ statement(ast->statement);
+ return false;
+}
+
+bool Codegen::visit(Block *ast)
+{
+ for (StatementList *it = ast->statements; it; it = it->next) {
+ statement(it->statement);
+ }
+ return false;
+}
+
+bool Codegen::visit(BreakStatement *ast)
+{
+ if (!_loop)
+ throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop"));
+ 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)
+ throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString()));
+ }
+ unwindException(loop->scopeAndFinally);
+ _block->JUMP(loop->breakBlock);
+ return false;
+}
+
+bool Codegen::visit(ContinueStatement *ast)
+{
+ 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(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString()));
+ }
+ if (!loop)
+ throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop"));
+ unwindException(loop->scopeAndFinally);
+ _block->JUMP(loop->continueBlock);
+ return false;
+}
+
+bool Codegen::visit(DebuggerStatement *)
+{
+ Q_UNIMPLEMENTED();
+ return false;
+}
+
+bool Codegen::visit(DoWhileStatement *ast)
+{
+ V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody);
+ V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, loopbody, loopend, loopcond);
+
+ _block->JUMP(loopbody);
+
+ _block = loopbody;
+ statement(ast->statement);
+ _block->JUMP(loopcond);
+
+ _block = loopcond;
+ condition(ast->expression, loopbody, loopend);
+
+ _block = loopend;
+
+ leaveLoop();
+
+ return false;
+}
+
+bool Codegen::visit(EmptyStatement *)
+{
+ return false;
+}
+
+bool Codegen::visit(ExpressionStatement *ast)
+{
+ if (_mode == EvalCode || _mode == QmlBinding) {
+ Result e = expression(ast->expression);
+ if (*e)
+ move(_block->TEMP(_returnAddress), *e);
+ } else {
+ statement(ast->expression);
+ }
+ return false;
+}
+
+bool Codegen::visit(ForEachStatement *ast)
+{
+ V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin);
+ V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, foreachin, foreachend, foreachin);
+
+ int objectToIterateOn = _block->newTemp();
+ move(_block->TEMP(objectToIterateOn), *expression(ast->expression));
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(_block->TEMP(objectToIterateOn));
+
+ int iterator = _block->newTemp();
+ move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+
+ _block->JUMP(foreachin);
+
+ _block = foreachbody;
+ int temp = _block->newTemp();
+ move(*expression(ast->initialiser), _block->TEMP(temp));
+ statement(ast->statement);
+ _block->JUMP(foreachin);
+
+ _block = foreachin;
+
+ args = _function->New<V4IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args));
+ int null = _block->newTemp();
+ move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0));
+ cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend);
+ _block = foreachend;
+
+ leaveLoop();
+ return false;
+}
+
+bool Codegen::visit(ForStatement *ast)
+{
+ V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond);
+ V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond);
+ V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, forcond, forend, forstep);
+
+ statement(ast->initialiser);
+ _block->JUMP(forcond);
+
+ _block = forcond;
+ if (ast->condition)
+ condition(ast->condition, forbody, forend);
+ else
+ _block->JUMP(forbody);
+
+ _block = forbody;
+ statement(ast->statement);
+ _block->JUMP(forstep);
+
+ _block = forstep;
+ statement(ast->expression);
+ _block->JUMP(forcond);
+
+ _block = forend;
+
+ leaveLoop();
+
+ return false;
+}
+
+bool Codegen::visit(IfStatement *ast)
+{
+ V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0;
+ V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock());
+ condition(ast->expression, iftrue, ast->ko ? iffalse : endif);
+
+ _block = iftrue;
+ statement(ast->ok);
+ _block->JUMP(endif);
+
+ if (ast->ko) {
+ _block = iffalse;
+ statement(ast->ko);
+ _block->JUMP(endif);
+ }
+
+ _block = endif;
+
+ return false;
+}
+
+bool Codegen::visit(LabelledStatement *ast)
+{
+ // check that no outer loop contains the label
+ Loop *l = _loop;
+ while (l) {
+ if (l->labelledStatement->label == ast->label) {
+ QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
+ throwSyntaxError(ast->firstSourceLocation(), error);
+ }
+ l = l->parent;
+ }
+ _labelledStatement = ast;
+
+ if (AST::cast<AST::SwitchStatement *>(ast->statement) ||
+ AST::cast<AST::WhileStatement *>(ast->statement) ||
+ AST::cast<AST::DoWhileStatement *>(ast->statement) ||
+ AST::cast<AST::ForStatement *>(ast->statement) ||
+ AST::cast<AST::ForEachStatement *>(ast->statement) ||
+ AST::cast<AST::LocalForStatement *>(ast->statement) ||
+ AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
+ statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
+ } else {
+ V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock());
+ enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0);
+ statement(ast->statement);
+ _block->JUMP(breakBlock);
+ _block = breakBlock;
+ leaveLoop();
+ }
+
+ return false;
+}
+
+bool Codegen::visit(LocalForEachStatement *ast)
+{
+ V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin);
+ V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, foreachin, foreachend, foreachin);
+
+ variableDeclaration(ast->declaration);
+
+ int iterator = _block->newTemp();
+ move(_block->TEMP(iterator), *expression(ast->expression));
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+
+ _block->JUMP(foreachin);
+
+ _block = foreachbody;
+ int temp = _block->newTemp();
+ move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
+ statement(ast->statement);
+ _block->JUMP(foreachin);
+
+ _block = foreachin;
+
+ args = _function->New<V4IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args));
+ int null = _block->newTemp();
+ move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0));
+ cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend);
+ _block = foreachend;
+
+ leaveLoop();
+ return false;
+}
+
+bool Codegen::visit(LocalForStatement *ast)
+{
+ V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond);
+ V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond);
+ V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, forcond, forend, forstep);
+
+ variableDeclarationList(ast->declarations);
+ _block->JUMP(forcond);
+
+ _block = forcond;
+ if (ast->condition)
+ condition(ast->condition, forbody, forend);
+ else
+ _block->JUMP(forbody);
+
+ _block = forbody;
+ statement(ast->statement);
+ _block->JUMP(forstep);
+
+ _block = forstep;
+ statement(ast->expression);
+ _block->JUMP(forcond);
+
+ _block = forend;
+
+ leaveLoop();
+
+ return false;
+}
+
+bool Codegen::visit(ReturnStatement *ast)
+{
+ if (_mode != FunctionCode && _mode != QmlBinding)
+ throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function"));
+ if (ast->expression) {
+ Result expr = expression(ast->expression);
+ move(_block->TEMP(_returnAddress), *expr);
+ }
+ unwindException(0);
+
+ _block->JUMP(_exitBlock);
+ return false;
+}
+
+bool Codegen::visit(SwitchStatement *ast)
+{
+ V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock());
+
+ if (ast->block) {
+ Result lhs = expression(ast->expression);
+ V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock());
+ _block->JUMP(switchcond);
+ V4IR::BasicBlock *previousBlock = 0;
+
+ QHash<Node *, V4IR::BasicBlock *> blockMap;
+
+ enterLoop(ast, 0, switchend, 0);
+
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+
+ _block = _function->newBasicBlock(groupStartBlock());
+ 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(groupStartBlock());
+ 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;
+ }
+
+ for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+
+ _block = _function->newBasicBlock(groupStartBlock());
+ blockMap[clause] = _block;
+
+ if (previousBlock && !previousBlock->isTerminated())
+ previousBlock->JUMP(_block);
+
+ for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
+ statement(it2->statement);
+
+ previousBlock = _block;
+ }
+
+ leaveLoop();
+
+ _block->JUMP(switchend);
+
+ _block = switchcond;
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+ Result rhs = expression(clause->expression);
+ V4IR::BasicBlock *iftrue = blockMap[clause];
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock());
+ cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse);
+ _block = iffalse;
+ }
+
+ for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+ Result rhs = expression(clause->expression);
+ V4IR::BasicBlock *iftrue = blockMap[clause];
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock());
+ cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse);
+ _block = iffalse;
+ }
+
+ if (ast->block->defaultClause) {
+ _block->JUMP(blockMap[ast->block->defaultClause]);
+ }
+ }
+
+ _block->JUMP(switchend);
+
+ _block = switchend;
+ return false;
+}
+
+bool Codegen::visit(ThrowStatement *ast)
+{
+ Result expr = expression(ast->expression);
+ move(_block->TEMP(_returnAddress), *expr);
+ _block->JUMP(_throwBlock);
+ return false;
+}
+
+bool Codegen::visit(TryStatement *ast)
+{
+ _function->hasTry = true;
+
+ if (_function->isStrict && ast->catchExpression &&
+ (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments")))
+ throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode"));
+
+ V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock());
+ // We always need a finally body to clean up the exception handler
+ V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock());
+
+ V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock());
+ V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>();
+ throwArgs->expr = throwBlock->TEMP(_returnAddress);
+ throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
+ throwBlock->JUMP(catchBody);
+ qSwap(_throwBlock, throwBlock);
+
+ int hasException = _block->newTemp();
+ move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false));
+
+ // Pass the hidden "needRethrow" TEMP to the
+ // builtin_delete_exception_handler, in order to have those TEMPs alive for
+ // the duration of the exception handling block.
+ V4IR::ExprList *finishTryArgs = _function->New<V4IR::ExprList>();
+ finishTryArgs->init(_block->TEMP(hasException));
+
+ ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs);
+ _scopeAndFinally = &tcf;
+
+ int exception_to_rethrow = _block->newTemp();
+
+ _block->TRY(tryBody, catchBody,
+ ast->catchExpression ? ast->catchExpression->name.toString() : QString(),
+ _block->TEMP(exception_to_rethrow));
+
+ _block = tryBody;
+ statement(ast->statement);
+ _block->JUMP(finallyBody);
+
+ _block = catchBody;
+
+ if (ast->catchExpression) {
+ // check if an exception got thrown within catch. Go to finally
+ // and then rethrow
+ V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock());
+ _block->CJUMP(_block->TEMP(hasException), finallyBody, b);
+ _block = b;
+ }
+
+ move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true));
+
+ if (ast->catchExpression) {
+ ++_function->insideWithOrCatch;
+ {
+ ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope);
+ _scopeAndFinally = &scope;
+ statement(ast->catchExpression->statement);
+ _scopeAndFinally = scope.parent;
+ }
+ --_function->insideWithOrCatch;
+ move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false));
+ }
+ _block->JUMP(finallyBody);
+
+ _scopeAndFinally = tcf.parent;
+
+ qSwap(_throwBlock, throwBlock);
+
+ V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock());
+ _block = finallyBody;
+
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs));
+
+ if (ast->finallyExpression && ast->finallyExpression->statement)
+ statement(ast->finallyExpression->statement);
+
+ V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock());
+ _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after);
+ _block = rethrowBlock;
+ move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow));
+ _block->JUMP(_throwBlock);
+
+ _block = after;
+
+ 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:
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0)));
+ // fall through
+ case ScopeAndFinally::CatchScope:
+ _scopeAndFinally = _scopeAndFinally->parent;
+ --_function->insideWithOrCatch;
+ break;
+ case ScopeAndFinally::TryScope: {
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs));
+ 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)
+{
+ variableDeclarationList(ast->declarations);
+ return false;
+}
+
+bool Codegen::visit(WhileStatement *ast)
+{
+ V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock());
+ V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond);
+ V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock());
+
+ enterLoop(ast, whilecond, whileend, whilecond);
+
+ _block->JUMP(whilecond);
+ _block = whilecond;
+ condition(ast->expression, whilebody, whileend);
+
+ _block = whilebody;
+ statement(ast->statement);
+ _block->JUMP(whilecond);
+
+ _block = whileend;
+ leaveLoop();
+
+ return false;
+}
+
+bool Codegen::visit(WithStatement *ast)
+{
+ _function->hasWith = true;
+
+ V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock());
+
+ _block->JUMP(withBlock);
+ _block = withBlock;
+ int withObject = _block->newTemp();
+ _block->MOVE(_block->TEMP(withObject), *expression(ast->expression));
+ V4IR::ExprList *args = _function->New<V4IR::ExprList>();
+ args->init(_block->TEMP(withObject));
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args));
+
+ ++_function->insideWithOrCatch;
+ {
+ ScopeAndFinally scope(_scopeAndFinally);
+ _scopeAndFinally = &scope;
+ statement(ast->statement);
+ _scopeAndFinally = scope.parent;
+ }
+ --_function->insideWithOrCatch;
+ _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0));
+
+ V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock());
+ _block->JUMP(next);
+ _block = next;
+
+ return false;
+}
+
+bool Codegen::visit(UiArrayBinding *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(UiObjectBinding *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(UiObjectDefinition *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(UiPublicMember *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(UiScriptBinding *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(UiSourceElement *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc)
+{
+ if (!_env->isStrict)
+ return;
+ V4IR::Name *n = expr->asName();
+ if (!n)
+ return;
+ if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments"))
+ throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode"));
+}
+
+void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
+{
+ QV4::DiagnosticMessage *msg = new QV4::DiagnosticMessage;
+ msg->fileName = _fileName;
+ msg->offset = loc.begin();
+ msg->startLine = loc.startLine;
+ msg->startColumn = loc.startColumn;
+ msg->message = detail;
+ if (_context)
+ _context->throwSyntaxError(msg);
+ else if (_errorHandler)
+ _errorHandler->syntaxError(msg);
+ else
+ Q_ASSERT(!"No error handler available.");
+}
+
+void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail)
+{
+ if (_context)
+ _context->throwReferenceError(QV4::Value::fromString(_context, detail), _fileName, loc.startLine);
+ else if (_errorHandler)
+ throwSyntaxError(loc, detail);
+ else
+ Q_ASSERT(!"No error handler available.");
+}
diff --git a/src/qml/qml/v4/qv4codegen_p.h b/src/qml/qml/v4/qv4codegen_p.h
new file mode 100644
index 0000000000..fe00d87852
--- /dev/null
+++ b/src/qml/qml/v4/qv4codegen_p.h
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4CODEGEN_P_H
+#define QV4CODEGEN_P_H
+
+#include "qv4global_p.h"
+#include "qv4jsir_p.h"
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+#include <QtCore/QStringList>
+#include <assert.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+struct DiagnosticMessage;
+struct ExecutionContext;
+}
+
+namespace QQmlJS {
+namespace AST {
+class UiParameterList;
+}
+
+
+
+class ErrorHandler
+{
+public:
+ virtual void syntaxError(QV4::DiagnosticMessage *message) = 0;
+};
+
+class Q_QML_EXPORT Codegen: protected AST::Visitor
+{
+public:
+ Codegen(QV4::ExecutionContext *ctx, bool strict);
+ Codegen(ErrorHandler *errorHandler, bool strictMode);
+
+ enum Mode {
+ GlobalCode,
+ EvalCode,
+ FunctionCode,
+ QmlBinding
+ };
+
+ V4IR::Function *operator()(const QString &fileName,
+ const QString &sourceCode,
+ AST::Program *ast,
+ V4IR::Module *module,
+ Mode mode = GlobalCode,
+ const QStringList &inheritedLocals = QStringList());
+ V4IR::Function *operator()(const QString &fileName,
+ const QString &sourceCode,
+ AST::FunctionExpression *ast,
+ V4IR::Module *module);
+
+protected:
+ enum Format { ex, cx, nx };
+ struct Result {
+ V4IR::Expr *code;
+ V4IR::BasicBlock *iftrue;
+ V4IR::BasicBlock *iffalse;
+ Format format;
+ Format requested;
+
+ explicit Result(Format requested = ex)
+ : code(0)
+ , iftrue(0)
+ , iffalse(0)
+ , format(ex)
+ , requested(requested) {}
+
+ explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse)
+ : code(0)
+ , iftrue(iftrue)
+ , iffalse(iffalse)
+ , format(ex)
+ , requested(cx) {}
+
+ inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
+ inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; }
+
+ bool accept(Format f)
+ {
+ if (requested == f) {
+ format = f;
+ return true;
+ }
+ return false;
+ }
+ };
+
+ struct Environment {
+ Environment *parent;
+
+ enum MemberType {
+ UndefinedMember,
+ VariableDefinition,
+ VariableDeclaration,
+ FunctionDefinition
+ };
+ struct Member {
+ MemberType type;
+ int index;
+ AST::FunctionExpression *function;
+ };
+ typedef QMap<QString, Member> MemberMap;
+
+ MemberMap members;
+ AST::FormalParameterList *formals;
+ int maxNumberOfArguments;
+ bool hasDirectEval;
+ bool hasNestedFunctions;
+ bool isStrict;
+ bool isNamedFunctionExpression;
+ enum UsesArgumentsObject {
+ ArgumentsObjectUnknown,
+ ArgumentsObjectNotUsed,
+ ArgumentsObjectUsed
+ };
+
+ UsesArgumentsObject usesArgumentsObject;
+
+ Environment(Environment *parent)
+ : parent(parent)
+ , formals(0)
+ , maxNumberOfArguments(0)
+ , hasDirectEval(false)
+ , hasNestedFunctions(false)
+ , isStrict(false)
+ , isNamedFunctionExpression(false)
+ , usesArgumentsObject(ArgumentsObjectUnknown)
+ {
+ if (parent && parent->isStrict)
+ isStrict = true;
+ }
+
+ int findMember(const QString &name) const
+ {
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end())
+ return -1;
+ assert((*it).index != -1 || !parent);
+ return (*it).index;
+ }
+
+ 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;
+ }
+
+ void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0)
+ {
+ 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;
+ members.insert(name, m);
+ } else {
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
+ }
+ }
+ }
+ };
+
+ Environment *newEnvironment(AST::Node *node, Environment *parent)
+ {
+ Environment *env = new Environment(parent);
+ _envMap.insert(node, env);
+ return env;
+ }
+
+ struct UiMember {
+ };
+
+ struct ScopeAndFinally {
+ enum ScopeType {
+ WithScope,
+ TryScope,
+ CatchScope
+ };
+
+ ScopeAndFinally *parent;
+ AST::Finally *finally;
+ V4IR::ExprList *finishTryArgs;
+ ScopeType type;
+
+ ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {}
+ ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs)
+ : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope)
+ {}
+ };
+
+ struct Loop {
+ AST::LabelledStatement *labelledStatement;
+ AST::Statement *node;
+ V4IR::BasicBlock *groupStartBlock;
+ V4IR::BasicBlock *breakBlock;
+ V4IR::BasicBlock *continueBlock;
+ Loop *parent;
+ ScopeAndFinally *scopeAndFinally;
+
+ Loop(AST::Statement *node, V4IR::BasicBlock *groupStartBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent)
+ : labelledStatement(0), node(node), groupStartBlock(groupStartBlock), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {}
+ };
+
+ void enterEnvironment(AST::Node *node);
+ void leaveEnvironment();
+
+ void enterLoop(AST::Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock);
+ void leaveLoop();
+ V4IR::BasicBlock *groupStartBlock() const
+ {
+ for (Loop *it = _loop; it; it = it->parent)
+ if (it->groupStartBlock)
+ return it->groupStartBlock;
+ return 0;
+ }
+
+ V4IR::Expr *member(V4IR::Expr *base, const QString *name);
+ V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index);
+ V4IR::Expr *argument(V4IR::Expr *expr);
+ V4IR::Expr *reference(V4IR::Expr *expr);
+ V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr);
+ V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right);
+ V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args);
+ void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid);
+ void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse);
+
+ V4IR::Function *defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::SourceElements *body,
+ Mode mode = FunctionCode,
+ const QStringList &inheritedLocals = QStringList());
+
+ void unwindException(ScopeAndFinally *outest);
+
+ void statement(AST::Statement *ast);
+ void statement(AST::ExpressionNode *ast);
+ void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse);
+ Result expression(AST::ExpressionNode *ast);
+ QString propertyName(AST::PropertyName *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 variableDeclaration(AST::VariableDeclaration *ast);
+ void variableDeclarationList(AST::VariableDeclarationList *ast);
+
+ V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0);
+
+ // nodes
+ virtual bool visit(AST::ArgumentList *ast);
+ virtual bool visit(AST::CaseBlock *ast);
+ virtual bool visit(AST::CaseClause *ast);
+ virtual bool visit(AST::CaseClauses *ast);
+ virtual bool visit(AST::Catch *ast);
+ virtual bool visit(AST::DefaultClause *ast);
+ virtual bool visit(AST::ElementList *ast);
+ virtual bool visit(AST::Elision *ast);
+ virtual bool visit(AST::Finally *ast);
+ virtual bool visit(AST::FormalParameterList *ast);
+ virtual bool visit(AST::FunctionBody *ast);
+ virtual bool visit(AST::Program *ast);
+ virtual bool visit(AST::PropertyNameAndValue *ast);
+ virtual bool visit(AST::PropertyAssignmentList *ast);
+ virtual bool visit(AST::PropertyGetterSetter *ast);
+ virtual bool visit(AST::SourceElements *ast);
+ virtual bool visit(AST::StatementList *ast);
+ virtual bool visit(AST::UiArrayMemberList *ast);
+ virtual bool visit(AST::UiImport *ast);
+ virtual bool visit(AST::UiImportList *ast);
+ virtual bool visit(AST::UiObjectInitializer *ast);
+ virtual bool visit(AST::UiObjectMemberList *ast);
+ virtual bool visit(AST::UiParameterList *ast);
+ virtual bool visit(AST::UiProgram *ast);
+ virtual bool visit(AST::UiQualifiedId *ast);
+ virtual bool visit(AST::VariableDeclaration *ast);
+ virtual bool visit(AST::VariableDeclarationList *ast);
+
+ // expressions
+ virtual bool visit(AST::Expression *ast);
+ virtual bool visit(AST::ArrayLiteral *ast);
+ virtual bool visit(AST::ArrayMemberExpression *ast);
+ virtual bool visit(AST::BinaryExpression *ast);
+ virtual bool visit(AST::CallExpression *ast);
+ virtual bool visit(AST::ConditionalExpression *ast);
+ virtual bool visit(AST::DeleteExpression *ast);
+ virtual bool visit(AST::FalseLiteral *ast);
+ virtual bool visit(AST::FieldMemberExpression *ast);
+ virtual bool visit(AST::FunctionExpression *ast);
+ virtual bool visit(AST::IdentifierExpression *ast);
+ virtual bool visit(AST::NestedExpression *ast);
+ virtual bool visit(AST::NewExpression *ast);
+ virtual bool visit(AST::NewMemberExpression *ast);
+ virtual bool visit(AST::NotExpression *ast);
+ virtual bool visit(AST::NullExpression *ast);
+ virtual bool visit(AST::NumericLiteral *ast);
+ virtual bool visit(AST::ObjectLiteral *ast);
+ virtual bool visit(AST::PostDecrementExpression *ast);
+ virtual bool visit(AST::PostIncrementExpression *ast);
+ virtual bool visit(AST::PreDecrementExpression *ast);
+ virtual bool visit(AST::PreIncrementExpression *ast);
+ virtual bool visit(AST::RegExpLiteral *ast);
+ virtual bool visit(AST::StringLiteral *ast);
+ virtual bool visit(AST::ThisExpression *ast);
+ virtual bool visit(AST::TildeExpression *ast);
+ virtual bool visit(AST::TrueLiteral *ast);
+ virtual bool visit(AST::TypeOfExpression *ast);
+ virtual bool visit(AST::UnaryMinusExpression *ast);
+ virtual bool visit(AST::UnaryPlusExpression *ast);
+ virtual bool visit(AST::VoidExpression *ast);
+ virtual bool visit(AST::FunctionDeclaration *ast);
+
+ // property names
+ virtual bool visit(AST::IdentifierPropertyName *ast);
+ virtual bool visit(AST::NumericLiteralPropertyName *ast);
+ virtual bool visit(AST::StringLiteralPropertyName *ast);
+
+ // source elements
+ virtual bool visit(AST::FunctionSourceElement *ast);
+ virtual bool visit(AST::StatementSourceElement *ast);
+
+ // statements
+ virtual bool visit(AST::Block *ast);
+ virtual bool visit(AST::BreakStatement *ast);
+ virtual bool visit(AST::ContinueStatement *ast);
+ virtual bool visit(AST::DebuggerStatement *ast);
+ virtual bool visit(AST::DoWhileStatement *ast);
+ virtual bool visit(AST::EmptyStatement *ast);
+ virtual bool visit(AST::ExpressionStatement *ast);
+ virtual bool visit(AST::ForEachStatement *ast);
+ virtual bool visit(AST::ForStatement *ast);
+ virtual bool visit(AST::IfStatement *ast);
+ virtual bool visit(AST::LabelledStatement *ast);
+ virtual bool visit(AST::LocalForEachStatement *ast);
+ virtual bool visit(AST::LocalForStatement *ast);
+ virtual bool visit(AST::ReturnStatement *ast);
+ virtual bool visit(AST::SwitchStatement *ast);
+ virtual bool visit(AST::ThrowStatement *ast);
+ virtual bool visit(AST::TryStatement *ast);
+ virtual bool visit(AST::VariableStatement *ast);
+ virtual bool visit(AST::WhileStatement *ast);
+ virtual bool visit(AST::WithStatement *ast);
+
+ // ui object members
+ virtual bool visit(AST::UiArrayBinding *ast);
+ virtual bool visit(AST::UiObjectBinding *ast);
+ virtual bool visit(AST::UiObjectDefinition *ast);
+ virtual bool visit(AST::UiPublicMember *ast);
+ virtual bool visit(AST::UiScriptBinding *ast);
+ virtual bool visit(AST::UiSourceElement *ast);
+
+ void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc);
+
+ void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
+ void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
+
+private:
+ QString _fileName;
+ Result _expr;
+ QString _property;
+ UiMember _uiMember;
+ V4IR::Module *_module;
+ V4IR::Function *_function;
+ V4IR::BasicBlock *_block;
+ V4IR::BasicBlock *_exitBlock;
+ V4IR::BasicBlock *_throwBlock;
+ unsigned _returnAddress;
+ Mode _mode;
+ Environment *_env;
+ Loop *_loop;
+ AST::LabelledStatement *_labelledStatement;
+ ScopeAndFinally *_scopeAndFinally;
+ QHash<AST::Node *, Environment *> _envMap;
+ QHash<AST::FunctionExpression *, int> _functionMap;
+ QV4::ExecutionContext *_context;
+ bool _strictMode;
+ ErrorHandler *_errorHandler;
+
+ class ScanFunctions;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp
deleted file mode 100644
index d6ab73acc2..0000000000
--- a/src/qml/qml/v4/qv4compiler.cpp
+++ /dev/null
@@ -1,1580 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4compiler_p.h"
-#include "qv4compiler_p_p.h"
-#include "qv4program_p.h"
-#include "qv4ir_p.h"
-#include "qv4irbuilder_p.h"
-
-#include <private/qqmlglobal_p.h>
-#include <private/qqmljsast_p.h>
-#include <private/qqmlaccessors_p.h>
-#include <private/qqmljsengine_p.h>
-
-QT_BEGIN_NAMESPACE
-
-DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP)
-DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER)
-DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
-DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST)
-
-static bool qmlBindingsTest = false;
-static bool qmlEnableV4 = true;
-
-using namespace QQmlJS;
-QV4CompilerPrivate::QV4CompilerPrivate()
- : subscriptionOffset(0)
- , _function(0) , _block(0) , _discarded(false), registerCount(0)
- , bindingLine(0), bindingColumn(0), invalidatable(false)
-{
-}
-
-//
-// tracing
-//
-void QV4CompilerPrivate::trace(quint16 line, quint16 column)
-{
- bytecode.clear();
-
- this->bindingLine = line;
- this->bindingColumn = column;
- this->currentReg = _function->tempCount;
- this->registerCount = qMax(this->registerCount, this->currentReg);
-
- foreach (IR::BasicBlock *bb, _function->basicBlocks) {
- if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size())
- bb->JUMP(_function->basicBlocks.at(bb->index + 1));
- }
-
- QVector<IR::BasicBlock *> blocks;
- trace(&blocks);
- currentBlockMask = 0x00000001;
-
-
- for (int i = 0; !_discarded && i < blocks.size(); ++i) {
- IR::BasicBlock *block = blocks.at(i);
- IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0;
- if (IR::Stmt *terminator = block->terminator()) {
- if (IR::CJump *cj = terminator->asCJump()) {
- if (cj->iffalse != next) {
- IR::Jump *jump = _function->pool->New<IR::Jump>();
- jump->init(cj->iffalse);
- block->statements.append(jump);
- }
- } else if (IR::Jump *j = terminator->asJump()) {
- if (j->target == next) {
- block->statements.resize(block->statements.size() - 1);
- }
- }
- }
-
- block->offset = bytecode.size();
-
- if (bytecode.isEmpty()) {
- if (qmlBindingsTest || bindingsDump()) {
- Instr::BindingId id;
- id.column = column;
- id.line = line;
- gen(id);
- }
-
- if (qmlBindingsTest) {
- QString str = expression->expression.asScript();
- QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
- int offset = data.count();
- data += strdata;
-
- Instr::EnableV4Test test;
- test.reg = 0;
- test.offset = offset;
- test.length = str.length();
- gen(test);
- }
- }
-
- bool usic = false;
- int patchesCount = patches.count();
- qSwap(usedSubscriptionIdsChanged, usic);
-
- int blockopIndex = bytecode.size();
- Instr::Block blockop;
- blockop.block = currentBlockMask;
- gen(blockop);
-
- foreach (IR::Stmt *s, block->statements) {
- if (! _discarded)
- s->accept(this);
- }
-
- qSwap(usedSubscriptionIdsChanged, usic);
-
- if (usic) {
- if (currentBlockMask == 0x80000000) {
- discard();
- return;
- }
- currentBlockMask <<= 1;
- } else if (! _discarded) {
- const int adjust = bytecode.remove(blockopIndex);
- // Correct patches
- for (int ii = patchesCount; ii < patches.count(); ++ii)
- patches[ii].offset -= adjust;
- }
- }
-
-#ifdef DEBUG_IR_STRUCTURE
- IR::IRDump dump;
- for (int i = 0; i < blocks.size(); ++i) {
- dump.basicblock(blocks.at(i));
- }
-#endif
-
-
- if (! _discarded) {
- // back patching
- foreach (const Patch &patch, patches) {
- V4Instr &instr = bytecode[patch.offset];
- int size = V4Instr::size(instructionType(&instr));
- instr.branchop.offset = patch.block->offset - patch.offset - size;
- }
-
- patches.clear();
- }
-}
-
-void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks)
-{
- for (int i = 0; i < _function->basicBlocks.size(); ++i) {
- IR::BasicBlock *block = _function->basicBlocks.at(i);
-
- while (! blocks->contains(block)) {
- blocks->append(block);
-
- if (IR::Stmt *terminator = block->terminator()) {
- if (IR::CJump *cj = terminator->asCJump())
- block = cj->iffalse;
- else if (IR::Jump *j = terminator->asJump())
- block = j->target;
- }
- }
- }
-}
-
-void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r)
-{
- if (!e) {
- discard();
- } else {
- qSwap(currentReg, r);
- e->accept(this);
- qSwap(currentReg, r);
- }
-}
-
-//
-// expressions
-//
-void QV4CompilerPrivate::visitConst(IR::Const *e)
-{
- switch (e->type) {
- case IR::BoolType: {
- Instr::LoadBool i;
- i.reg = currentReg;
- i.value = e->value;
- gen(i);
- } break;
-
- case IR::IntType: {
- Instr::LoadInt i;
- i.reg = currentReg;
- i.value = e->value;
- gen(i);
- } break;
-
- case IR::FloatType:
- case IR::NumberType: {
- Instr::LoadNumber i;
- i.reg = currentReg;
- i.value = e->value;
- gen(i);
- } break;
-
- case IR::NullType: {
- Instr::LoadNull i;
- i.reg = currentReg;
- gen(i);
- } break;
-
- default:
- if (qmlVerboseCompiler())
- qWarning() << Q_FUNC_INFO << "unexpected type";
- discard();
- }
-}
-
-void QV4CompilerPrivate::visitString(IR::String *e)
-{
- registerLiteralString(currentReg, e->value);
-}
-
-void QV4CompilerPrivate::visitName(IR::Name *e)
-{
- if (e->base) {
- // fetch the object and store it in reg.
- traceExpression(e->base, currentReg);
- } else {
- _subscribeName.clear();
- }
-
- if (e->storage == IR::Name::RootStorage) {
-
- Instr::LoadRoot instr;
- instr.reg = currentReg;
- gen(instr);
-
- if (e->symbol == IR::Name::IdObject) {
- // The ID is a reference to the root object
- return;
- }
-
- } else if (e->storage == IR::Name::ScopeStorage) {
-
- Instr::LoadScope instr;
- instr.reg = currentReg;
- gen(instr);
-
- _subscribeName << contextName();
-
- } else if (e->storage == IR::Name::IdStorage) {
-
- Instr::LoadId instr;
- instr.reg = currentReg;
- instr.index = e->idObject->idIndex;
- gen(instr);
-
- _subscribeName << QLatin1String("$$$ID_") + *e->id;
-
- if (blockNeedsSubscription(_subscribeName)) {
- Instr::SubscribeId sub;
- sub.reg = currentReg;
- sub.offset = subscriptionIndex(_subscribeName);
- sub.index = instr.index;
- gen(sub);
- }
-
- return;
- } else {
- // No action needed
- }
-
- switch (e->symbol) {
- case IR::Name::Unbound:
- case IR::Name::IdObject:
- case IR::Name::Slot:
- case IR::Name::Object: {
- Q_ASSERT(!"Unreachable");
- discard();
- } break;
-
- case IR::Name::AttachType: {
- _subscribeName << *e->id;
-
- Instr::LoadAttached attached;
- attached.output = currentReg;
- attached.reg = currentReg;
- attached.exceptionId = exceptionId(bindingLine, bindingColumn);
- if (e->declarativeType->attachedPropertiesId() == -1)
- discard();
- attached.id = e->declarativeType->attachedPropertiesId();
- gen(attached);
- } break;
-
- case IR::Name::SingletonObject: {
- /*
- Existing singleton type object lookup methods include:
- 1. string -> singleton object (search via importCache->query(name))
- 2. typeid -> singleton object QQmlType (search via ???)
- We currently use 1, which is not ideal for performance
- */
- _subscribeName << *e->id;
-
- registerLiteralString(currentReg, e->id);
-
- Instr::LoadSingletonObject module;
- module.reg = currentReg;
- gen(module);
- } break;
-
- case IR::Name::Property: {
- _subscribeName << *e->id;
-
- if (e->property->coreIndex == -1) {
- QMetaProperty prop;
- e->property->load(prop, QQmlEnginePrivate::get(engine));
- }
-
- const int propTy = e->property->propType;
- QQmlRegisterType regType;
-
- switch (propTy) {
- case QMetaType::Float:
- regType = FloatType;
- break;
- case QMetaType::Double:
- regType = NumberType;
- break;
- case QMetaType::Bool:
- regType = BoolType;
- break;
- case QMetaType::Int:
- regType = IntType;
- break;
- case QMetaType::QString:
- regType = QStringType;
- break;
- case QMetaType::QUrl:
- regType = QUrlType;
- break;
- case QMetaType::QColor:
- regType = QColorType;
- break;
- case QMetaType::QVariant:
- regType = QVariantType;
- break;
-
- default:
- if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
- regType = PODValueType;
- } else if (!engine->metaObjectForType(propTy).isNull()) {
- regType = QObjectStarType;
- } else {
- if (qmlVerboseCompiler())
- qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy);
- discard(); // Unsupported type
- return;
- }
-
- break;
- } // switch
-
- if (e->property->hasAccessors()) {
- Instr::FetchAndSubscribe fetch;
- fetch.reg = currentReg;
- fetch.subscription = subscriptionIndex(_subscribeName);
- fetch.exceptionId = exceptionId(e->line, e->column);
- fetch.valueType = regType;
- fetch.property = *e->property;
- gen(fetch);
- } else {
- Instr::Fetch fetch;
- fetch.reg = currentReg;
- fetch.index = e->property->coreIndex;
- fetch.exceptionId = exceptionId(e->line, e->column);
- fetch.valueType = regType;
-
- if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) {
- fetch.subOffset = subscriptionIndex(_subscribeName);
- fetch.subIndex = e->property->notifyIndex;
- } else {
- fetch.subOffset = static_cast<quint16>(-1);
- fetch.subIndex = static_cast<quint32>(-1);
- }
-
- gen(fetch);
- }
-
- } break;
- } // switch
-}
-
-void QV4CompilerPrivate::visitTemp(IR::Temp *e)
-{
- if (currentReg != e->index) {
- Instr::Copy i;
- i.reg = currentReg;
- i.src = e->index;
- gen(i);
- }
-}
-
-void QV4CompilerPrivate::visitUnop(IR::Unop *e)
-{
- quint8 src = currentReg;
-
- if (IR::Temp *temp = e->expr->asTemp()) {
- src = temp->index;
- } else {
- traceExpression(e->expr, src);
- }
-
- switch (e->op) {
- case IR::OpInvalid:
- Q_ASSERT(!"unreachable");
- break;
-
- case IR::OpIfTrue:
- convertToBool(e->expr, src);
- if (src != currentReg) {
- Instr::Copy i;
- i.reg = currentReg;
- i.src = src;
- gen(i);
- }
- break;
-
- case IR::OpNot: {
- Instr::UnaryNot i;
- convertToBool(e->expr, src);
- i.output = currentReg;
- i.src = src;
- gen(i);
- } break;
-
- case IR::OpUMinus:
- if (IR::isRealType(e->expr->type)) {
- Instr::UnaryMinusNumber i;
- i.output = currentReg;
- i.src = src;
- gen(i);
- } else if (e->expr->type == IR::IntType) {
- convertToNumber(e->expr, currentReg);
- Instr::UnaryMinusNumber i;
- i.output = currentReg;
- i.src = src;
- gen(i);
- } else {
- discard();
- }
- break;
-
- case IR::OpUPlus:
- if (IR::isRealType(e->expr->type)) {
- Instr::UnaryPlusNumber i;
- i.output = currentReg;
- i.src = src;
- gen(i);
- } else if (e->expr->type == IR::IntType) {
- convertToNumber(e->expr, currentReg);
- Instr::UnaryPlusNumber i;
- i.output = currentReg;
- i.src = src;
- gen(i);
- } else {
- discard();
- }
- break;
-
- case IR::OpCompl:
- // TODO
- discard();
- break;
-
- case IR::OpBitAnd:
- case IR::OpBitOr:
- case IR::OpBitXor:
- case IR::OpAdd:
- case IR::OpSub:
- case IR::OpMul:
- case IR::OpDiv:
- case IR::OpMod:
- case IR::OpLShift:
- case IR::OpRShift:
- case IR::OpURShift:
- case IR::OpGt:
- case IR::OpLt:
- case IR::OpGe:
- case IR::OpLe:
- case IR::OpEqual:
- case IR::OpNotEqual:
- case IR::OpStrictEqual:
- case IR::OpStrictNotEqual:
- case IR::OpAnd:
- case IR::OpOr:
- Q_ASSERT(!"unreachable");
- break;
- } // switch
-}
-
-void QV4CompilerPrivate::convertToNumber(IR::Expr *expr, int reg)
-{
- if (expr->type == IR::NumberType)
- return;
-
- switch (expr->type) {
- case IR::BoolType: {
- Instr::ConvertBoolToNumber i;
- i.output = i.src = reg;
- gen(i);
- } break;
-
- case IR::IntType: {
- Instr::ConvertIntToNumber i;
- i.output = i.src = reg;
- gen(i);
- } break;
-
- case IR::FloatType:
- case IR::NumberType:
- // nothing to do
- return;
-
- default:
- discard();
- break;
- } // switch
-}
-
-void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg)
-{
- if (expr->type == IR::IntType)
- return;
-
- switch (expr->type) {
- case IR::BoolType: {
- Instr::ConvertBoolToInt i;
- i.output = i.src = reg;
- gen(i);
- } break;
-
- case IR::IntType:
- // nothing to do
- return;
-
- case IR::FloatType:
- case IR::NumberType: {
- Instr::ConvertNumberToInt i;
- i.output = i.src = reg;
- gen(i);
- } break;
-
- default:
- discard();
- break;
- } // switch
-}
-
-void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg)
-{
- if (expr->type == IR::BoolType)
- return;
-
- switch (expr->type) {
- case IR::BoolType:
- // nothing to do
- break;
-
- case IR::IntType: {
- Instr::ConvertIntToBool i;
- i.output = i.src = reg;
- gen(i);
- } break;
-
- case IR::FloatType:
- case IR::NumberType: {
- Instr::ConvertNumberToBool i;
- i.output = i.src = reg;
- gen(i);
- } return;
-
- case IR::StringType: {
- Instr::ConvertStringToBool i;
- i.output = i.src = reg;
- gen(i);
- } return;
-
- case IR::ColorType: {
- Instr::ConvertColorToBool i;
- i.output = i.src = reg;
- gen(i);
- } return;
-
- default:
- discard();
- break;
- } // switch
-}
-
-quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e)
-{
- switch (e->op) {
- case IR::OpInvalid:
- return V4Instr::Noop;
-
- case IR::OpIfTrue:
- case IR::OpNot:
- case IR::OpUMinus:
- case IR::OpUPlus:
- case IR::OpCompl:
- return V4Instr::Noop;
-
- case IR::OpBitAnd:
- return V4Instr::BitAndInt;
-
- case IR::OpBitOr:
- return V4Instr::BitOrInt;
-
- case IR::OpBitXor:
- return V4Instr::BitXorInt;
-
- case IR::OpAdd:
- if (e->type == IR::StringType)
- return V4Instr::AddString;
- return V4Instr::AddNumber;
-
- case IR::OpSub:
- return V4Instr::SubNumber;
-
- case IR::OpMul:
- return V4Instr::MulNumber;
-
- case IR::OpDiv:
- return V4Instr::DivNumber;
-
- case IR::OpMod:
- return V4Instr::ModNumber;
-
- case IR::OpLShift:
- return V4Instr::LShiftInt;
-
- case IR::OpRShift:
- return V4Instr::RShiftInt;
-
- case IR::OpURShift:
- return V4Instr::URShiftInt;
-
- case IR::OpGt:
- if (e->left->type == IR::StringType)
- return V4Instr::GtString;
- return V4Instr::GtNumber;
-
- case IR::OpLt:
- if (e->left->type == IR::StringType)
- return V4Instr::LtString;
- return V4Instr::LtNumber;
-
- case IR::OpGe:
- if (e->left->type == IR::StringType)
- return V4Instr::GeString;
- return V4Instr::GeNumber;
-
- case IR::OpLe:
- if (e->left->type == IR::StringType)
- return V4Instr::LeString;
- return V4Instr::LeNumber;
-
- case IR::OpEqual:
- if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
- return V4Instr::EqualObject;
- if (e->left->type == IR::StringType)
- return V4Instr::EqualString;
- return V4Instr::EqualNumber;
-
- case IR::OpNotEqual:
- if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
- return V4Instr::NotEqualObject;
- if (e->left->type == IR::StringType)
- return V4Instr::NotEqualString;
- return V4Instr::NotEqualNumber;
-
- case IR::OpStrictEqual:
- if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
- return V4Instr::StrictEqualObject;
- if (e->left->type == IR::StringType)
- return V4Instr::StrictEqualString;
- return V4Instr::StrictEqualNumber;
-
- case IR::OpStrictNotEqual:
- if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType)
- return V4Instr::StrictNotEqualObject;
- if (e->left->type == IR::StringType)
- return V4Instr::StrictNotEqualString;
- return V4Instr::StrictNotEqualNumber;
-
- case IR::OpAnd:
- case IR::OpOr:
- return V4Instr::Noop;
-
- } // switch
-
- return V4Instr::Noop;
-}
-
-void QV4CompilerPrivate::visitBinop(IR::Binop *e)
-{
- if (e->type == IR::InvalidType) {
- discard();
- return;
- }
-
- int left = currentReg;
- int right = currentReg + 1;
-
- if (e->left->asTemp() && e->type != IR::StringType)
- left = e->left->asTemp()->index;
- else
- traceExpression(e->left, left);
-
- if (IR::Temp *t = e->right->asTemp())
- right = t->index;
- else
- traceExpression(e->right, right);
-
- if (e->left->type != e->right->type) {
- if (qmlVerboseCompiler())
- qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op)
- << "(`" << IR::binaryOperator(e->left->type)
- << "' and `"
- << IR::binaryOperator(e->right->type)
- << '\'';
- discard();
- return;
- }
-
- switch (e->op) {
- case IR::OpInvalid:
- discard();
- break;
-
- // unary
- case IR::OpIfTrue:
- case IR::OpNot:
- case IR::OpUMinus:
- case IR::OpUPlus:
- case IR::OpCompl:
- discard();
- break;
-
- case IR::OpBitAnd:
- case IR::OpBitOr:
- case IR::OpBitXor:
- case IR::OpLShift:
- case IR::OpRShift:
- case IR::OpURShift:
- convertToInt(e->left, left);
- convertToInt(e->right, right);
- break;
-
- case IR::OpAdd:
- if (e->type != IR::StringType) {
- convertToNumber(e->left, left);
- convertToNumber(e->right, right);
- }
- break;
-
- case IR::OpSub:
- case IR::OpMul:
- case IR::OpDiv:
- case IR::OpMod:
- convertToNumber(e->left, left);
- convertToNumber(e->right, right);
- break;
-
- case IR::OpGt:
- case IR::OpLt:
- case IR::OpGe:
- case IR::OpLe:
- case IR::OpEqual:
- case IR::OpNotEqual:
- case IR::OpStrictEqual:
- case IR::OpStrictNotEqual:
- if (e->left->type >= IR::FirstNumberType) {
- convertToNumber(e->left, left);
- convertToNumber(e->right, right);
- }
- break;
-
- case IR::OpAnd:
- case IR::OpOr:
- discard(); // ### unreachable
- break;
- } // switch
-
- const quint8 opcode = instructionOpcode(e);
- if (opcode != V4Instr::Noop) {
- V4Instr instr;
- instr.binaryop.output = currentReg;
- instr.binaryop.left = left;
- instr.binaryop.right = right;
- gen(static_cast<V4Instr::Type>(opcode), instr);
- }
-}
-
-void QV4CompilerPrivate::visitCall(IR::Call *call)
-{
- if (IR::Name *name = call->base->asName()) {
- IR::Expr *arg = call->onlyArgument();
- if (arg != 0 && IR::isRealType(arg->type)) {
- traceExpression(arg, currentReg);
-
- switch (name->builtin) {
- case IR::NoBuiltinSymbol:
- break;
-
- case IR::MathSinBultinFunction: {
- Instr::MathSinNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathCosBultinFunction: {
- Instr::MathCosNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathAbsBuiltinFunction: {
- Instr::MathAbsNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathRoundBultinFunction: {
- Instr::MathRoundNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathFloorBultinFunction: {
- Instr::MathFloorNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathCeilBuiltinFunction: {
- Instr::MathCeilNumber i;
- i.output = i.src = currentReg;
- gen(i);
- } return;
-
- case IR::MathPIBuiltinConstant:
- default:
- break;
- } // switch
- } else {
- if (name->builtin == IR::MathMaxBuiltinFunction ||
- name->builtin == IR::MathMinBuiltinFunction) {
-
- //only handles the most common case of exactly two arguments
- if (call->args && call->args->next && !call->args->next->next) {
- IR::Expr *arg1 = call->args->expr;
- IR::Expr *arg2 = call->args->next->expr;
-
- if (arg1 != 0 && IR::isRealType(arg1->type) &&
- arg2 != 0 && IR::isRealType(arg2->type)) {
-
- traceExpression(arg1, currentReg);
- traceExpression(arg2, currentReg + 1);
-
- if (name->builtin == IR::MathMaxBuiltinFunction) {
- Instr::MathMaxNumber i;
- i.left = currentReg;
- i.right = currentReg + 1;
- i.output = currentReg;
- gen(i);
- return;
- } else if (name->builtin == IR::MathMinBuiltinFunction) {
- Instr::MathMinNumber i;
- i.left = currentReg;
- i.right = currentReg + 1;
- i.output = currentReg;
- gen(i);
- return;
- }
- }
- }
- }
- }
- }
-
- if (qmlVerboseCompiler())
- qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__;
- discard();
-}
-
-
-//
-// statements
-//
-void QV4CompilerPrivate::visitExp(IR::Exp *s)
-{
- traceExpression(s->expr, currentReg);
-}
-
-void QV4CompilerPrivate::visitMove(IR::Move *s)
-{
- IR::Temp *target = s->target->asTemp();
- Q_ASSERT(target != 0);
-
- quint8 dest = target->index;
-
- IR::Type targetTy = s->target->type;
- IR::Type sourceTy = s->source->type;
-
- // promote the floats
- if (sourceTy == IR::FloatType)
- sourceTy = IR::NumberType;
-
- if (targetTy == IR::FloatType)
- targetTy = IR::NumberType;
-
- if (sourceTy != targetTy) {
- quint8 src = dest;
-
- if (IR::Temp *t = s->source->asTemp())
- src = t->index;
- else
- traceExpression(s->source, dest);
-
- V4Instr::Type opcode = V4Instr::Noop;
-
- if (sourceTy == IR::UrlType) {
- switch (targetTy) {
- case IR::BoolType:
- case IR::StringType:
- case IR::VariantType:
- case IR::VarType:
- case IR::JSValueType:
- // nothing to do. V4 will generate optimized
- // url-to-xxx conversions.
- break;
- default: {
- if (s->isMoveForReturn) {
- V4Instr instr;
- instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn);
- registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2")
- .arg(QLatin1String(IR::typeName(sourceTy)))
- .arg(QLatin1String(IR::typeName(targetTy)))));
- instr.throwop.message = dest;
- gen(V4Instr::Throw, instr);
- return;
- }
- // generate a UrlToString conversion and fix
- // the type of the source expression.
- V4Instr conv;
- conv.unaryop.output = src;
- conv.unaryop.src = src;
- gen(V4Instr::ConvertUrlToString, conv);
- sourceTy = IR::StringType;
- break;
- }
- } // switch
- }
-
- if (targetTy == IR::BoolType) {
- switch (sourceTy) {
- case IR::IntType: opcode = V4Instr::ConvertIntToBool; break;
- case IR::NumberType: opcode = V4Instr::ConvertNumberToBool; break;
- case IR::StringType: opcode = V4Instr::ConvertStringToBool; break;
- case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break;
- case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break;
- case IR::ObjectType: opcode = V4Instr::ConvertObjectToBool; break;
- default: break;
- } // switch
- } else if (targetTy == IR::IntType) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break;
- case IR::NumberType: {
- if (s->isMoveForReturn)
- opcode = V4Instr::MathRoundNumber;
- else
- opcode = V4Instr::ConvertNumberToInt;
- break;
- }
- case IR::StringType: opcode = V4Instr::ConvertStringToInt; break;
- default: break;
- } // switch
- } else if (IR::isRealType(targetTy)) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToNumber; break;
- case IR::IntType: opcode = V4Instr::ConvertIntToNumber; break;
- case IR::StringType: opcode = V4Instr::ConvertStringToNumber; break;
- default: break;
- } // switch
- } else if (targetTy == IR::StringType) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break;
- case IR::IntType: opcode = V4Instr::ConvertIntToString; break;
- case IR::NumberType: opcode = V4Instr::ConvertNumberToString; break;
- case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break;
- case IR::ColorType: opcode = V4Instr::ConvertColorToString; break;
- default: break;
- } // switch
- } else if (targetTy == IR::UrlType) {
- if (s->isMoveForReturn && sourceTy != IR::StringType) {
- V4Instr instr;
- instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn);
- registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2")
- .arg(QLatin1String(IR::typeName(sourceTy)))
- .arg(QLatin1String(IR::typeName(targetTy)))));
- instr.throwop.message = dest;
- gen(V4Instr::Throw, instr);
- return;
- }
-
- V4Instr convToString;
- convToString.unaryop.output = dest;
- convToString.unaryop.src = src;
-
- // try to convert the source expression to a string.
- switch (sourceTy) {
- case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break;
- case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break;
- case IR::NumberType: gen(V4Instr::ConvertNumberToString, convToString); sourceTy = IR::StringType; break;
- case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break;
- default: break;
- } // switch
-
- if (sourceTy == IR::StringType)
- opcode = V4Instr::ConvertStringToUrl;
- } else if (targetTy == IR::ColorType) {
- switch (sourceTy) {
- case IR::StringType: opcode = V4Instr::ConvertStringToColor; break;
- default: break;
- } // switch
- } else if (targetTy == IR::ObjectType) {
- switch (sourceTy) {
- case IR::NullType: opcode = V4Instr::ConvertNullToObject; break;
- default: break;
- } // switch
- } else if (targetTy == IR::VariantType) {
- if (s->isMoveForReturn) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToVariant; break;
- case IR::IntType: opcode = V4Instr::ConvertIntToVariant; break;
- case IR::NumberType: opcode = V4Instr::ConvertNumberToVariant; break;
- case IR::UrlType: opcode = V4Instr::ConvertUrlToVariant; break;
- case IR::ColorType: opcode = V4Instr::ConvertColorToVariant; break;
- case IR::StringType: opcode = V4Instr::ConvertStringToVariant; break;
- case IR::ObjectType: opcode = V4Instr::ConvertObjectToVariant; break;
- case IR::NullType: opcode = V4Instr::ConvertNullToVariant; break;
- default: break;
- } // switch
- }
- } else if (targetTy == IR::VarType) {
- if (s->isMoveForReturn) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToVar; break;
- case IR::IntType: opcode = V4Instr::ConvertIntToVar; break;
- case IR::NumberType: opcode = V4Instr::ConvertNumberToVar; break;
- case IR::UrlType: opcode = V4Instr::ConvertUrlToVar; break;
- case IR::ColorType: opcode = V4Instr::ConvertColorToVar; break;
- case IR::StringType: opcode = V4Instr::ConvertStringToVar; break;
- case IR::ObjectType: opcode = V4Instr::ConvertObjectToVar; break;
- case IR::NullType: opcode = V4Instr::ConvertNullToVar; break;
- case IR::JSValueType: opcode = V4Instr::ConvertJSValueToVar; break;
- default: break;
- } // switch
- }
- } else if (targetTy == IR::JSValueType) {
- if (s->isMoveForReturn) {
- switch (sourceTy) {
- case IR::BoolType: opcode = V4Instr::ConvertBoolToJSValue; break;
- case IR::IntType: opcode = V4Instr::ConvertIntToJSValue; break;
- case IR::NumberType: opcode = V4Instr::ConvertNumberToJSValue; break;
- case IR::UrlType: opcode = V4Instr::ConvertUrlToJSValue; break;
- case IR::ColorType: opcode = V4Instr::ConvertColorToJSValue; break;
- case IR::StringType: opcode = V4Instr::ConvertStringToJSValue; break;
- case IR::ObjectType: opcode = V4Instr::ConvertObjectToJSValue; break;
- case IR::VarType: opcode = V4Instr::ConvertVarToJSValue; break;
- case IR::NullType: opcode = V4Instr::ConvertNullToJSValue; break;
- default: break;
- }
- }
- }
- if (opcode != V4Instr::Noop) {
- V4Instr conv;
- conv.unaryop.output = dest;
- conv.unaryop.src = src;
- gen(opcode, conv);
-
- if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) {
- V4Instr resolveUrl;
- resolveUrl.unaryop.output = dest;
- resolveUrl.unaryop.src = dest;
- gen(V4Instr::ResolveUrl, resolveUrl);
- }
- } else {
- discard();
- }
- } else {
- traceExpression(s->source, dest);
- }
-}
-
-void QV4CompilerPrivate::visitJump(IR::Jump *s)
-{
- patches.append(Patch(s->target, bytecode.size()));
-
- Instr::Branch i;
- i.offset = 0; // ### backpatch
- gen(i);
-}
-
-void QV4CompilerPrivate::visitCJump(IR::CJump *s)
-{
- traceExpression(s->cond, currentReg);
-
- patches.append(Patch(s->iftrue, bytecode.size()));
-
- Instr::BranchTrue i;
- i.reg = currentReg;
- i.offset = 0; // ### backpatch
- gen(i);
-}
-
-void QV4CompilerPrivate::visitRet(IR::Ret *s)
-{
- Q_ASSERT(s->expr != 0);
-
- int storeReg = currentReg;
-
- if (IR::Temp *temp = s->expr->asTemp()) {
- storeReg = temp->index;
- } else {
- traceExpression(s->expr, storeReg);
- }
-
- if (qmlBindingsTest) {
- Instr::TestV4Store test;
- test.reg = storeReg;
- switch (s->type) {
- case IR::StringType:
- test.regType = QMetaType::QString;
- break;
- case IR::UrlType:
- test.regType = QMetaType::QUrl;
- break;
- case IR::ColorType:
- test.regType = QMetaType::QColor;
- break;
- case IR::SGAnchorLineType:
- test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId();
- break;
- case IR::ObjectType:
- test.regType = QMetaType::QObjectStar;
- break;
- case IR::VariantType:
- test.regType = QMetaType::QVariant;
- break;
- case IR::VarType:
- test.regType = qMetaTypeId<v8::Handle<v8::Value> >();
- break;
- case IR::JSValueType:
- test.regType = qMetaTypeId<QJSValue>();
- break;
- case IR::BoolType:
- test.regType = QMetaType::Bool;
- break;
- case IR::IntType:
- test.regType = QMetaType::Int;
- break;
- case IR::FloatType:
- case IR::NumberType:
- test.regType = QMetaType::Double;
- break;
- default:
- discard();
- return;
- }
- gen(test);
- }
-
- Instr::Store store;
- store.output = 0;
- store.index = expression->property->index;
- store.reg = storeReg;
- store.valueType = s->type == IR::FloatType ? FloatType : 0;
- store.exceptionId = exceptionId(s->line, s->column);
- gen(store);
-}
-
-void QV4Compiler::dump(const QByteArray &programData)
-{
- const QV4Program *program = (const QV4Program *)programData.constData();
-
- qWarning() << "Program.bindings:" << program->bindings;
- qWarning() << "Program.dataLength:" << program->dataLength;
- qWarning() << "Program.subscriptions:" << program->subscriptions;
-
- const int programSize = program->instructionCount;
- const char *start = program->instructions();
- const char *end = start + programSize;
- Bytecode bc;
- bc.dump(start, end);
-}
-
-/*!
-Clear the state associated with attempting to compile a specific binding.
-This does not clear the global "committed binding" states.
-*/
-void QV4CompilerPrivate::resetInstanceState()
-{
- data = committed.data;
- exceptions = committed.exceptions;
- usedSubscriptionIds.clear();
- subscriptionIds.clear();
- subscriptionOffset = committed.subscriptionCount;
- bytecode.clear();
- patches.clear();
- pool.clear();
- currentReg = 0;
- invalidatable = false;
-}
-
-/*!
-Mark the last compile as successful, and add it to the "committed data"
-section.
-
-Returns the index for the committed binding.
-*/
-int QV4CompilerPrivate::commitCompile()
-{
- int rv = committed.count();
- committed.offsets << committed.bytecode.count();
- committed.dependencies << usedSubscriptionIds;
- committed.bytecode.append(bytecode.constData(), bytecode.size());
- committed.data = data;
- committed.exceptions = exceptions;
- committed.subscriptionCount = subscriptionOffset + subscriptionIds.count();
- if (bindingsDump())
- committed.subscriptions.append(subscriptionIds);
- return rv;
-}
-
-bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node)
-{
- resetInstanceState();
-
- if (expression->property->type == -1)
- return false;
-
- AST::SourceLocation location;
- if (AST::ExpressionNode *astExpression = node->expressionCast()) {
- location = astExpression->firstSourceLocation();
- } else if (AST::Statement *astStatement = node->statementCast()) {
- if (AST::Block *block = AST::cast<AST::Block *>(astStatement))
- location = block->lbraceToken;
- else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement))
- location = ifStmt->ifToken;
- else
- return false;
- } else {
- return false;
- }
-
- IR::Function thisFunction(&pool), *function = &thisFunction;
-
- QV4IRBuilder irBuilder(expression, engine);
- if (!irBuilder(function, node, &invalidatable))
- return false;
-
- bool discarded = false;
- qSwap(_discarded, discarded);
- qSwap(_function, function);
- trace(location.startLine, location.startColumn);
- qSwap(_function, function);
- qSwap(_discarded, discarded);
-
- if (qmlVerboseCompiler()) {
- QTextStream qerr(stderr, QIODevice::WriteOnly);
- if (discarded)
- qerr << "======== TODO ====== " << endl;
- else
- qerr << "==================== " << endl;
- qerr << "\tline: " << location.startLine
- << "\tcolumn: " << location.startColumn
- << endl;
- foreach (IR::BasicBlock *bb, function->basicBlocks)
- bb->dump(qerr);
- qerr << endl;
- }
-
- if (discarded || subscriptionIds.count() > 0xFFFF || registerCount > 31)
- return false;
-
- return true;
-}
-
-// Returns a reg
-int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str)
-{
- // ### string cleanup
-
- QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar));
- int offset = data.count();
- data += strdata;
-
- Instr::LoadString string;
- string.reg = reg;
- string.offset = offset;
- string.length = str.length();
- gen(string);
-
- return reg;
-}
-
-/*!
-Returns true if the current expression has not already subscribed to \a sub in currentBlockMask.
-*/
-bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub)
-{
- QString str = sub.join(QLatin1String("."));
-
- int *iter = subscriptionIds.value(str);
- if (!iter)
- return true;
-
- quint32 *uiter = usedSubscriptionIds.value(*iter);
- if (!uiter)
- return true;
- else
- return !(*uiter & currentBlockMask);
-}
-
-int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub)
-{
- QString str = sub.join(QLatin1String("."));
- int *iter = subscriptionIds.value(str);
- if (!iter) {
- int count = subscriptionOffset + subscriptionIds.count();
- iter = &subscriptionIds[str];
- *iter = count;
- }
- quint32 &u = usedSubscriptionIds[*iter];
- if (!(u & currentBlockMask)) {
- u |= currentBlockMask;
- usedSubscriptionIdsChanged = true;
- }
- return *iter;
-}
-
-quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub)
-{
- QString str = sub.join(QLatin1String("."));
-
- int *iter = subscriptionIds.value(str);
- Q_ASSERT(iter != 0);
-
- quint32 *uiter = usedSubscriptionIds.value(*iter);
- Q_ASSERT(uiter != 0);
-
- return *uiter;
-}
-
-quint8 QV4CompilerPrivate::exceptionId(quint16 line, quint16 column)
-{
- quint8 rv = 0xFF;
- if (exceptions.count() < 0xFF) {
- rv = (quint8)exceptions.count();
- quint32 e = line;
- e <<= 16;
- e |= column;
- exceptions.append(e);
- }
- return rv;
-}
-
-quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n)
-{
- quint8 rv = 0xFF;
- if (n && exceptions.count() < 0xFF) {
- QQmlJS::AST::SourceLocation l = n->firstSourceLocation();
- rv = exceptionId(l.startLine, l.startColumn);
- }
- return rv;
-}
-
-QV4Compiler::QV4Compiler()
-: d(new QV4CompilerPrivate)
-{
- qmlBindingsTest |= qmlBindingsTestEnv();
-}
-
-QV4Compiler::~QV4Compiler()
-{
- delete d; d = 0;
-}
-
-/*
-Returns true if any bindings were compiled.
-*/
-bool QV4Compiler::isValid() const
-{
- return !d->committed.bytecode.isEmpty();
-}
-
-/*
--1 on failure, otherwise the binding index to use.
-*/
-int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine, bool *invalidatable)
-{
- if (!expression.expression.asAST()) return false;
-
- if (qmlDisableOptimizer() || !qmlEnableV4)
- return -1;
-
- d->expression = &expression;
- d->engine = engine;
-
- if (d->compile(expression.expression.asAST())) {
- *invalidatable = d->isInvalidatable();
- return d->commitCompile();
- } else {
- return -1;
- }
-}
-
-QByteArray QV4CompilerPrivate::buildSignalTable() const
-{
- QHash<int, QList<QPair<int, quint32> > > table;
-
- for (int ii = 0; ii < committed.count(); ++ii) {
- const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii);
- for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter)
- table[iter->first].append(qMakePair(ii, iter->second));
- }
-
- QVector<quint32> header;
- QVector<quint32> data;
- for (int ii = 0; ii < committed.subscriptionCount; ++ii) {
- header.append(committed.subscriptionCount + data.count());
- const QList<QPair<int, quint32> > &bindings = table[ii];
- data.append(bindings.count());
- for (int jj = 0; jj < bindings.count(); ++jj) {
- data.append(bindings.at(jj).first);
- data.append(bindings.at(jj).second);
- }
- }
- header << data;
-
- return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
-}
-
-QByteArray QV4CompilerPrivate::buildExceptionData() const
-{
- QByteArray rv;
- rv.resize(committed.exceptions.count() * sizeof(quint32));
- ::memcpy(rv.data(), committed.exceptions.constData(), rv.size());
- return rv;
-}
-
-/*
-Returns the compiled program.
-*/
-QByteArray QV4Compiler::program() const
-{
- QByteArray programData;
-
- if (isValid()) {
- QV4Program prog;
- prog.bindings = d->committed.count();
-
- Bytecode bc;
- QV4CompilerPrivate::Instr::Jump jump;
- jump.reg = -1;
-
- for (int ii = 0; ii < d->committed.count(); ++ii) {
- jump.count = d->committed.count() - ii - 1;
- jump.count*= V4InstrMeta<V4Instr::Jump>::Size;
- jump.count+= d->committed.offsets.at(ii);
- bc.append(jump);
- }
-
-
- QByteArray bytecode;
- bytecode.reserve(bc.size() + d->committed.bytecode.size());
- bytecode.append(bc.constData(), bc.size());
- bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size());
-
- QByteArray data = d->committed.data;
- while (data.count() % 4) data.append('\0');
- prog.signalTableOffset = data.count();
- data += d->buildSignalTable();
- while (data.count() % 4) data.append('\0');
- prog.exceptionDataOffset = data.count();
- data += d->buildExceptionData();
-
- prog.dataLength = 4 * ((data.size() + 3) / 4);
- prog.subscriptions = d->committed.subscriptionCount;
- prog.instructionCount = bytecode.count();
- int size = sizeof(QV4Program) + bytecode.count();
- size += prog.dataLength;
-
- programData.resize(size);
- memcpy(programData.data(), &prog, sizeof(QV4Program));
- if (prog.dataLength)
- memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(),
- data.size());
- memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(),
- bytecode.count());
- }
-
- if (bindingsDump()) {
- qWarning().nospace() << "Subscription slots:";
-
- QQmlAssociationList<QString, int> subscriptionIds;
- foreach (subscriptionIds, d->committed.subscriptions) {
- for (QQmlAssociationList<QString, int>::ConstIterator iter = subscriptionIds.begin();
- iter != subscriptionIds.end(); ++iter) {
- qWarning().nospace() << " " << iter->first << "\t-> " << iter->second;
- }
- }
- QV4Compiler::dump(programData);
- }
-
- return programData;
-}
-
-void QV4Compiler::enableBindingsTest(bool e)
-{
- if (e)
- qmlBindingsTest = true;
- else
- qmlBindingsTest = qmlBindingsTestEnv();
-}
-
-void QV4Compiler::enableV4(bool e)
-{
- qmlEnableV4 = e;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h
deleted file mode 100644
index 58ec521a97..0000000000
--- a/src/qml/qml/v4/qv4compiler_p_p.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4COMPILER_P_P_H
-#define QV4COMPILER_P_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 "qv4instruction_p.h"
-#include "qv4ir_p.h"
-#include <private/qqmlscript_p.h>
-#include <private/qqmlimport_p.h>
-#include <private/qqmlengine_p.h>
-
-QT_BEGIN_NAMESPACE
-
-template <typename _Key, typename _Value>
-class QQmlAssociationList
-{
-public:
- typedef QVarLengthArray<QPair<_Key, _Value>, 8> Container;
- typedef typename Container::const_iterator const_iterator;
- typedef typename Container::const_iterator ConstIterator;
-
- const_iterator begin() const { return _container.begin(); }
- const_iterator end() const { return _container.end(); }
- int count() const { return _container.count(); }
- void clear() { _container.clear(); }
-
- _Value *value(const _Key &key) {
- for (int i = 0; i < _container.size(); ++i) {
- QPair<_Key, _Value> &p = _container[i];
- if (p.first == key)
- return &p.second;
- }
- return 0;
- }
-
- _Value &operator[](const _Key &key) {
- for (int i = 0; i < _container.size(); ++i) {
- QPair<_Key, _Value> &p = _container[i];
- if (p.first == key)
- return p.second;
- }
- int index = _container.size();
- _container.append(qMakePair(key, _Value()));
- return _container[index].second;
- }
-
- void insert(const _Key &key, _Value &value) {
- for (int i = 0; i < _container.size(); ++i) {
- QPair<_Key, _Value> &p = _container[i];
- if (p.first == key) {
- p.second = value;
- return;
- }
- }
- _container.append(qMakePair(key, value));
- }
-
-private:
- Container _container;
-};
-
-class QV4CompilerPrivate: protected QQmlJS::IR::ExprVisitor,
- protected QQmlJS::IR::StmtVisitor
-{
-public:
- QV4CompilerPrivate();
-
- void resetInstanceState();
- int commitCompile();
-
- const QV4Compiler::Expression *expression;
- QQmlEnginePrivate *engine;
-
- QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)expression->context, 16); }
-
- bool compile(QQmlJS::AST::Node *);
-
- bool isInvalidatable() const { return invalidatable; }
-
- int registerLiteralString(quint8 reg, const QStringRef &);
- QByteArray data;
-
- bool blockNeedsSubscription(const QStringList &);
- int subscriptionIndex(const QStringList &);
- quint32 subscriptionBlockMask(const QStringList &);
-
- quint8 exceptionId(quint16 line, quint16 column);
- quint8 exceptionId(QQmlJS::AST::ExpressionNode *);
- QVector<quint32> exceptions;
-
- QQmlAssociationList<int, quint32> usedSubscriptionIds;
- int subscriptionOffset;
- QQmlAssociationList<QString, int> subscriptionIds;
- QQmlJS::Bytecode bytecode;
-
- // back patching
- struct Patch {
- QQmlJS::IR::BasicBlock *block; // the basic block
- int offset; // the index of the instruction to patch
- Patch(QQmlJS::IR::BasicBlock *block = 0, int index = -1)
- : block(block), offset(index) {}
- };
- QVector<Patch> patches;
- QQmlPool pool;
-
- // Committed binding data
- struct Committed {
- Committed(): subscriptionCount(0) {}
- QList<int> offsets;
- QList<QQmlAssociationList<int, quint32> > dependencies;
-
- //QQmlJS::Bytecode bytecode;
- QByteArray bytecode;
- QByteArray data;
- QVector<quint32> exceptions;
- int subscriptionCount;
- QList<QQmlAssociationList<QString, int> > subscriptions;
-
- int count() const { return offsets.count(); }
- } committed;
-
- QByteArray buildSignalTable() const;
- QByteArray buildExceptionData() const;
-
- void convertToNumber(QQmlJS::IR::Expr *expr, int reg);
- void convertToInt(QQmlJS::IR::Expr *expr, int reg);
- void convertToBool(QQmlJS::IR::Expr *expr, int reg);
- quint8 instructionOpcode(QQmlJS::IR::Binop *e);
-
- struct Instr {
-#define QML_V4_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlJS::V4InstrData<QQmlJS::V4Instr::I> I;
- FOR_EACH_V4_INSTR(QML_V4_INSTR_DATA_TYPEDEF)
-#undef QML_v4_INSTR_DATA_TYPEDEF
- private:
- Instr();
- };
-
-protected:
- //
- // tracing
- //
- void trace(quint16 line, quint16 column);
- void trace(QVector<QQmlJS::IR::BasicBlock *> *blocks);
- void traceExpression(QQmlJS::IR::Expr *e, quint8 r);
-
- template <int Instr>
- inline void gen(const QQmlJS::V4InstrData<Instr> &i)
- { bytecode.append(i); }
- inline void gen(QQmlJS::V4Instr::Type type, QQmlJS::V4Instr &instr)
- { bytecode.append(type, instr); }
-
- inline QQmlJS::V4Instr::Type instructionType(const QQmlJS::V4Instr *i) const
- { return bytecode.instructionType(i); }
-
- //
- // expressions
- //
- virtual void visitConst(QQmlJS::IR::Const *e);
- virtual void visitString(QQmlJS::IR::String *e);
- virtual void visitName(QQmlJS::IR::Name *e);
- virtual void visitTemp(QQmlJS::IR::Temp *e);
- virtual void visitUnop(QQmlJS::IR::Unop *e);
- virtual void visitBinop(QQmlJS::IR::Binop *e);
- virtual void visitCall(QQmlJS::IR::Call *e);
-
- //
- // statements
- //
- virtual void visitExp(QQmlJS::IR::Exp *s);
- virtual void visitMove(QQmlJS::IR::Move *s);
- virtual void visitJump(QQmlJS::IR::Jump *s);
- virtual void visitCJump(QQmlJS::IR::CJump *s);
- virtual void visitRet(QQmlJS::IR::Ret *s);
-
-private:
- QStringList _subscribeName;
- QQmlJS::IR::Function *_function;
- QQmlJS::IR::BasicBlock *_block;
- void discard() { _discarded = true; }
- bool _discarded;
- quint8 currentReg;
- quint8 registerCount;
-
- bool usedSubscriptionIdsChanged;
- quint32 currentBlockMask;
- quint16 bindingLine;
- quint16 bindingColumn;
- bool invalidatable;
-};
-
-
-QT_END_NAMESPACE
-
-#endif // QV4COMPILER_P_P_H
-
diff --git a/src/qml/qml/v4/qv4context.cpp b/src/qml/qml/v4/qv4context.cpp
new file mode 100644
index 0000000000..ed01548410
--- /dev/null
+++ b/src/qml/qml/v4/qv4context.cpp
@@ -0,0 +1,615 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QString>
+#include "qv4debugging_p.h"
+#include <qv4context_p.h>
+#include <qv4object_p.h>
+#include <qv4objectproto_p.h>
+#include "qv4mm_p.h"
+#include <qv4argumentsobject_p.h>
+#include "qv4function_p.h"
+
+using namespace QV4;
+
+DiagnosticMessage::DiagnosticMessage()
+ : offset(0)
+ , length(0)
+ , startLine(0)
+ , startColumn(0)
+ , type(0)
+ , next(0)
+{}
+
+DiagnosticMessage::~DiagnosticMessage()
+{
+ delete next;
+}
+
+String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const
+{
+ QString msg;
+ if (!fileName.isEmpty())
+ msg = fileName + QLatin1Char(':');
+ msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": ");
+ if (type == QV4::DiagnosticMessage::Error)
+ msg += QLatin1String("error");
+ else
+ msg += QLatin1String("warning");
+ msg += ": " + message;
+
+ return ctx->engine->newString(msg);
+}
+
+void ExecutionContext::createMutableBinding(String *name, bool deletable)
+{
+
+ // find the right context to create the binding on
+ Object *activation = engine->globalObject;
+ ExecutionContext *ctx = this;
+ while (ctx) {
+ if (ctx->type >= Type_CallContext) {
+ CallContext *c = static_cast<CallContext *>(ctx);
+ if (!c->activation)
+ c->activation = engine->newObject();
+ activation = c->activation;
+ break;
+ }
+ ctx = ctx->outer;
+ }
+
+ if (activation->__hasProperty__(name))
+ return;
+ Property desc = Property::fromValue(Value::undefinedValue());
+ PropertyAttributes attrs(Attr_Data);
+ attrs.setConfigurable(deletable);
+ activation->__defineOwnProperty__(this, name, desc, attrs);
+}
+
+String * const *ExecutionContext::formals() const
+{
+ return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterList : 0;
+}
+
+unsigned int ExecutionContext::formalCount() const
+{
+ return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterCount : 0;
+}
+
+String * const *ExecutionContext::variables() const
+{
+ return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varList : 0;
+}
+
+unsigned int ExecutionContext::variableCount() const
+{
+ return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varCount : 0;
+}
+
+
+void GlobalContext::initGlobalContext(ExecutionEngine *eng)
+{
+ initBaseContext(Type_GlobalContext, eng, /*parentContext*/0);
+ thisObject = Value::fromObject(eng->globalObject);
+ global = 0;
+}
+
+void WithContext::initWithContext(ExecutionContext *p, Object *with)
+{
+ initBaseContext(Type_WithContext, p->engine, p);
+ thisObject = p->thisObject;
+ outer = p;
+ lookups = p->lookups;
+
+ withObject = with;
+}
+
+void CatchContext::initCatchContext(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue)
+{
+ initBaseContext(Type_CatchContext, p->engine, p);
+ strictMode = p->strictMode;
+ thisObject = p->thisObject;
+ outer = p;
+ lookups = p->lookups;
+
+ this->exceptionVarName = exceptionVarName;
+ this->exceptionValue = exceptionValue;
+}
+
+void CallContext::initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *_arguments, int _argumentCount, const Value &_thisObject)
+{
+ initBaseContext(Type_CallContext, parentContext->engine, parentContext);
+
+ this->function = function;
+ this->arguments = _arguments;
+ this->argumentCount = _argumentCount;
+ this->thisObject = _thisObject;
+
+ strictMode = function->strictMode;
+ marked = false;
+ outer = function->scope;
+#ifndef QT_NO_DEBUG
+ assert(outer->next != (ExecutionContext *)0x1);
+#endif
+
+ activation = 0;
+
+ if (function->function)
+ lookups = function->function->lookups;
+
+ uint argc = argumentCount;
+
+ locals = (Value *)(this + 1);
+ if (function->varCount)
+ std::fill(locals, locals + function->varCount, Value::undefinedValue());
+
+ if (needsOwnArguments()) {
+ Value *args = arguments;
+ argumentCount = qMax(argc, function->formalParameterCount);
+ arguments = locals + function->varCount;
+ if (argc)
+ ::memcpy(arguments, args, argc * sizeof(Value));
+ if (argc < function->formalParameterCount)
+ std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
+
+ }
+
+ if (function->usesArgumentsObject) {
+ ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc);
+ args->prototype = engine->objectPrototype;
+ activation = engine->newObject();
+ Property desc = Property::fromValue(Value::fromObject(args));
+ activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable);
+ }
+}
+
+void CallContext::initQmlContext(ExecutionContext *parentContext, Object *qml, FunctionObject *function)
+{
+ initBaseContext(Type_QmlContext, parentContext->engine, parentContext);
+
+ this->function = function;
+ this->arguments = 0;
+ this->argumentCount = 0;
+ this->thisObject = Value::undefinedValue();
+
+ strictMode = true;
+ marked = false;
+ this->outer = function->scope;
+#ifndef QT_NO_DEBUG
+ assert(outer->next != (ExecutionContext *)0x1);
+#endif
+
+ activation = qml;
+
+ lookups = function->function->lookups;
+
+ locals = (Value *)(this + 1);
+ if (function->varCount)
+ std::fill(locals, locals + function->varCount, Value::undefinedValue());
+}
+
+
+bool ExecutionContext::deleteProperty(String *name)
+{
+ bool hasWith = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->type == Type_WithContext) {
+ hasWith = true;
+ WithContext *w = static_cast<WithContext *>(ctx);
+ if (w->withObject->__hasProperty__(name))
+ return w->withObject->deleteProperty(name);
+ } else if (ctx->type == Type_CatchContext) {
+ CatchContext *c = static_cast<CatchContext *>(ctx);
+ if (c->exceptionVarName->isEqualTo(name))
+ return false;
+ } else if (ctx->type >= Type_CallContext) {
+ CallContext *c = static_cast<CallContext *>(ctx);
+ FunctionObject *f = c->function;
+ if (f->needsActivation || hasWith) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return false;
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return false;
+ }
+ if (c->activation && c->activation->__hasProperty__(name))
+ return c->activation->deleteProperty(name);
+ } else if (ctx->type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(ctx);
+ if (g->global->__hasProperty__(name))
+ return g->global->deleteProperty(name);
+ }
+ }
+
+ if (strictMode)
+ throwSyntaxError(0);
+ return true;
+}
+
+bool CallContext::needsOwnArguments() const
+{
+ return function->needsActivation || argumentCount < function->formalParameterCount;
+}
+
+void ExecutionContext::mark()
+{
+ if (marked)
+ return;
+ marked = true;
+
+ if (type != Type_SimpleCallContext && outer)
+ outer->mark();
+
+ thisObject.mark();
+
+ if (type >= Type_SimpleCallContext) {
+ QV4::CallContext *c = static_cast<CallContext *>(this);
+ for (unsigned arg = 0, lastArg = c->argumentCount; arg < lastArg; ++arg)
+ c->arguments[arg].mark();
+ if (type >= Type_CallContext) {
+ for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local)
+ c->locals[local].mark();
+ if (c->activation)
+ c->activation->mark();
+ c->function->mark();
+ }
+ } else if (type == Type_WithContext) {
+ WithContext *w = static_cast<WithContext *>(this);
+ w->withObject->mark();
+ } else if (type == Type_CatchContext) {
+ CatchContext *c = static_cast<CatchContext *>(this);
+ if (c->exceptionVarName)
+ c->exceptionVarName->mark();
+ c->exceptionValue.mark();
+ } else if (type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(this);
+ g->global->mark();
+ }
+}
+
+void ExecutionContext::setProperty(String *name, const Value& value)
+{
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->type == Type_WithContext) {
+ Object *w = static_cast<WithContext *>(ctx)->withObject;
+ if (w->__hasProperty__(name)) {
+ w->put(name, value);
+ return;
+ }
+ } else if (ctx->type == Type_CatchContext && static_cast<CatchContext *>(ctx)->exceptionVarName->isEqualTo(name)) {
+ static_cast<CatchContext *>(ctx)->exceptionValue = value;
+ return;
+ } else {
+ Object *activation = 0;
+ if (ctx->type >= Type_CallContext) {
+ CallContext *c = static_cast<CallContext *>(ctx);
+ for (unsigned int i = 0; i < c->function->varCount; ++i)
+ if (c->function->varList[i]->isEqualTo(name)) {
+ c->locals[i] = value;
+ return;
+ }
+ for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i)
+ if (c->function->formalParameterList[i]->isEqualTo(name)) {
+ c->arguments[i] = value;
+ return;
+ }
+ activation = c->activation;
+ } else if (ctx->type == Type_GlobalContext) {
+ activation = static_cast<GlobalContext *>(ctx)->global;
+ }
+
+ if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) {
+ activation->put(name, value);
+ return;
+ }
+ }
+ }
+ if (strictMode || name->isEqualTo(engine->id_this))
+ throwReferenceError(Value::fromString(name));
+ engine->globalObject->put(name, value);
+}
+
+Value ExecutionContext::getProperty(String *name)
+{
+ name->makeIdentifier();
+
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+ bool hasCatchScope = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->type == Type_WithContext) {
+ Object *w = static_cast<WithContext *>(ctx)->withObject;
+ hasWith = true;
+ bool hasProperty = false;
+ Value v = w->get(name, &hasProperty);
+ if (hasProperty) {
+ return v;
+ }
+ continue;
+ }
+
+ else if (ctx->type == Type_CatchContext) {
+ hasCatchScope = true;
+ CatchContext *c = static_cast<CatchContext *>(ctx);
+ if (c->exceptionVarName->isEqualTo(name))
+ return c->exceptionValue;
+ }
+
+ else if (ctx->type >= Type_CallContext) {
+ QV4::CallContext *c = static_cast<CallContext *>(ctx);
+ FunctionObject *f = c->function;
+ if (f->needsActivation || hasWith || hasCatchScope) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return c->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return c->arguments[i];
+ }
+ if (c->activation) {
+ bool hasProperty = false;
+ Value v = c->activation->get(name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ if (f->function && f->function->isNamedExpression
+ && name->isEqualTo(f->function->name))
+ return Value::fromObject(c->function);
+ }
+
+ else if (ctx->type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(ctx);
+ bool hasProperty = false;
+ Value v = g->global->get(name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ throwReferenceError(Value::fromString(name));
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyNoThrow(String *name)
+{
+ name->makeIdentifier();
+
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+ bool hasCatchScope = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->type == Type_WithContext) {
+ Object *w = static_cast<WithContext *>(ctx)->withObject;
+ hasWith = true;
+ bool hasProperty = false;
+ Value v = w->get(name, &hasProperty);
+ if (hasProperty) {
+ return v;
+ }
+ continue;
+ }
+
+ else if (ctx->type == Type_CatchContext) {
+ hasCatchScope = true;
+ CatchContext *c = static_cast<CatchContext *>(ctx);
+ if (c->exceptionVarName->isEqualTo(name))
+ return c->exceptionValue;
+ }
+
+ else if (ctx->type >= Type_CallContext) {
+ QV4::CallContext *c = static_cast<CallContext *>(ctx);
+ FunctionObject *f = c->function;
+ if (f->needsActivation || hasWith || hasCatchScope) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return c->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return c->arguments[i];
+ }
+ if (c->activation) {
+ bool hasProperty = false;
+ Value v = c->activation->get(name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ if (f->function && f->function->isNamedExpression
+ && name->isEqualTo(f->function->name))
+ return Value::fromObject(c->function);
+ }
+
+ else if (ctx->type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(ctx);
+ bool hasProperty = false;
+ Value v = g->global->get(name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyAndBase(String *name, Object **base)
+{
+ *base = 0;
+ name->makeIdentifier();
+
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+ bool hasCatchScope = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->type == Type_WithContext) {
+ Object *w = static_cast<WithContext *>(ctx)->withObject;
+ hasWith = true;
+ bool hasProperty = false;
+ Value v = w->get(name, &hasProperty);
+ if (hasProperty) {
+ *base = w;
+ return v;
+ }
+ continue;
+ }
+
+ else if (ctx->type == Type_CatchContext) {
+ hasCatchScope = true;
+ CatchContext *c = static_cast<CatchContext *>(ctx);
+ if (c->exceptionVarName->isEqualTo(name))
+ return c->exceptionValue;
+ }
+
+ else if (ctx->type >= Type_CallContext) {
+ QV4::CallContext *c = static_cast<CallContext *>(ctx);
+ FunctionObject *f = c->function;
+ if (f->needsActivation || hasWith || hasCatchScope) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return c->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return c->arguments[i];
+ }
+ if (c->activation) {
+ bool hasProperty = false;
+ Value v = c->activation->get(name, &hasProperty);
+ if (hasProperty) {
+ *base = c->activation;
+ return v;
+ }
+ }
+ if (f->function && f->function->isNamedExpression
+ && name->isEqualTo(f->function->name))
+ return Value::fromObject(c->function);
+ }
+
+ else if (ctx->type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(ctx);
+ bool hasProperty = false;
+ Value v = g->global->get(name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ throwReferenceError(Value::fromString(name));
+ return Value::undefinedValue();
+}
+
+
+
+void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op)
+{
+ Value lhs = getProperty(name);
+ Value result;
+ op(this, &result, lhs, value);
+ setProperty(name, result);
+}
+
+void ExecutionContext::throwError(const Value &value)
+{
+ __qmljs_throw(this, value);
+}
+
+void ExecutionContext::throwError(const QString &message)
+{
+ Value v = Value::fromString(this, message);
+ throwError(Value::fromObject(engine->newErrorObject(v)));
+}
+
+void ExecutionContext::throwSyntaxError(DiagnosticMessage *message)
+{
+ throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message)));
+}
+
+void ExecutionContext::throwTypeError()
+{
+ throwError(Value::fromObject(engine->newTypeErrorObject(QStringLiteral("Type error"))));
+}
+
+void ExecutionContext::throwTypeError(const QString &message)
+{
+ throwError(Value::fromObject(engine->newTypeErrorObject(message)));
+}
+
+void ExecutionContext::throwUnimplemented(const QString &message)
+{
+ Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message);
+ throwError(Value::fromObject(engine->newErrorObject(v)));
+}
+
+void ExecutionContext::throwReferenceError(Value value)
+{
+ String *s = value.toString(this);
+ QString msg = s->toQString() + QStringLiteral(" is not defined");
+ throwError(Value::fromObject(engine->newReferenceErrorObject(msg)));
+}
+
+void ExecutionContext::throwReferenceError(Value value, const QString &fileName, int line)
+{
+ String *s = value.toString(this);
+ QString msg = s->toQString() + QStringLiteral(" is not defined");
+ throwError(Value::fromObject(engine->newReferenceErrorObject(msg, fileName, line)));
+}
+
+void ExecutionContext::throwRangeError(Value value)
+{
+ String *s = value.toString(this);
+ QString msg = s->toQString() + QStringLiteral(" out of range");
+ throwError(Value::fromObject(engine->newRangeErrorObject(msg)));
+}
+
+void ExecutionContext::throwURIError(Value msg)
+{
+ throwError(Value::fromObject(engine->newURIErrorObject(msg)));
+}
+
+
+void SimpleCallContext::initSimpleCallContext(ExecutionEngine *engine)
+{
+ initBaseContext(Type_SimpleCallContext, engine, engine->current);
+ function = 0;
+ arguments = 0;
+ argumentCount = 0;
+}
diff --git a/src/qml/qml/v4/qv4context_p.h b/src/qml/qml/v4/qv4context_p.h
new file mode 100644
index 0000000000..dfe02bdcc8
--- /dev/null
+++ b/src/qml/qml/v4/qv4context_p.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_ENVIRONMENT_H
+#define QMLJS_ENVIRONMENT_H
+
+#include "qv4global_p.h"
+#include "qv4runtime_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Object;
+struct ExecutionEngine;
+struct DeclarativeEnvironment;
+struct Lookup;
+
+struct Q_QML_EXPORT DiagnosticMessage
+{
+ enum { Error, Warning };
+
+ QString fileName;
+ quint32 offset;
+ quint32 length;
+ quint32 startLine;
+ unsigned startColumn: 31;
+ unsigned type: 1;
+ QString message;
+ DiagnosticMessage *next;
+
+ DiagnosticMessage();
+ ~DiagnosticMessage();
+ String *buildFullMessage(ExecutionContext *ctx) const;
+};
+
+struct CallContext;
+
+struct Q_QML_EXPORT ExecutionContext
+{
+ enum Type {
+ Type_GlobalContext = 0x1,
+ Type_CatchContext = 0x2,
+ Type_WithContext = 0x3,
+ Type_SimpleCallContext = 0x4,
+ Type_CallContext = 0x5,
+ Type_QmlContext = 0x6
+ };
+
+ Type type;
+ bool strictMode;
+ bool marked;
+
+ Value thisObject;
+
+ ExecutionEngine *engine;
+ ExecutionContext *parent;
+ ExecutionContext *outer;
+ Lookup *lookups;
+ ExecutionContext *next; // used in the GC
+
+ struct EvalCode
+ {
+ Function *function;
+ EvalCode *next;
+ };
+ EvalCode *currentEvalCode;
+
+ const uchar **interpreterInstructionPointer;
+
+ void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext)
+ {
+ this->type = type;
+ strictMode = false;
+ marked = false;
+ thisObject = Value::undefinedValue();
+ this->engine = engine;
+ parent = parentContext;
+ outer = 0;
+ lookups = 0;
+ currentEvalCode = 0;
+ interpreterInstructionPointer = 0;
+ }
+
+ String * const *formals() const;
+ unsigned int formalCount() const;
+ String * const *variables() const;
+ unsigned int variableCount() const;
+
+ void createMutableBinding(String *name, bool deletable);
+
+ void Q_NORETURN throwError(const Value &value);
+ void Q_NORETURN throwError(const QString &message);
+ void Q_NORETURN throwSyntaxError(DiagnosticMessage *message);
+ void Q_NORETURN throwTypeError();
+ void Q_NORETURN throwTypeError(const QString &message);
+ void Q_NORETURN throwReferenceError(Value value);
+ void Q_NORETURN throwReferenceError(Value value, const QString &fileName, int line);
+ void Q_NORETURN throwRangeError(Value value);
+ void Q_NORETURN throwURIError(Value msg);
+ void Q_NORETURN throwUnimplemented(const QString &message);
+
+ void setProperty(String *name, const Value &value);
+ Value getProperty(String *name);
+ Value getPropertyNoThrow(String *name);
+ Value getPropertyAndBase(String *name, Object **base);
+ void inplaceBitOp(String *name, const QV4::Value &value, BinOp op);
+ bool deleteProperty(String *name);
+
+ inline Value argument(unsigned int index = 0);
+
+ void mark();
+
+ inline CallContext *asCallContext();
+ inline const CallContext *asCallContext() const;
+};
+
+struct SimpleCallContext : public ExecutionContext
+{
+ void initSimpleCallContext(ExecutionEngine *engine);
+ FunctionObject *function;
+ Value *arguments;
+ unsigned int argumentCount;
+};
+
+struct CallContext : public SimpleCallContext
+{
+ void initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *args, int argc,
+ const Value &thisObject);
+ void initQmlContext(ExecutionContext *parentContext, Object *qml, QV4::FunctionObject *function);
+ bool needsOwnArguments() const;
+
+ Value *locals;
+ Object *activation;
+};
+
+struct GlobalContext : public ExecutionContext
+{
+ void initGlobalContext(ExecutionEngine *e);
+
+ Object *global;
+};
+
+struct CatchContext : public ExecutionContext
+{
+ void initCatchContext(ExecutionContext *p, String *exceptionVarName, const QV4::Value &exceptionValue);
+
+ String *exceptionVarName;
+ Value exceptionValue;
+};
+
+struct WithContext : public ExecutionContext
+{
+ Object *withObject;
+
+ void initWithContext(ExecutionContext *p, Object *with);
+};
+
+inline Value ExecutionContext::argument(unsigned int index)
+{
+ if (type >= Type_SimpleCallContext) {
+ CallContext *ctx = static_cast<CallContext *>(this);
+ if (index < ctx->argumentCount)
+ return ctx->arguments[index];
+ }
+ return Value::undefinedValue();
+}
+
+inline CallContext *ExecutionContext::asCallContext()
+{
+ return type >= Type_CallContext ? static_cast<CallContext *>(this) : 0;
+}
+
+inline const CallContext *ExecutionContext::asCallContext() const
+{
+ return type >= Type_CallContext ? static_cast<const CallContext *>(this) : 0;
+}
+
+/* Function *f, int argc */
+#define requiredMemoryForExecutionContect(f, argc) \
+ sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount))
+#define requiredMemoryForQmlExecutionContect(f) \
+ sizeof(CallContext) + sizeof(Value) * (f->locals.size())
+#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value))
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4dateobject.cpp b/src/qml/qml/v4/qv4dateobject.cpp
new file mode 100644
index 0000000000..3cf6cb1aeb
--- /dev/null
+++ b/src/qml/qml/v4/qv4dateobject.cpp
@@ -0,0 +1,1316 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4dateobject_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4mm_p.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDateTime>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+#include <cmath>
+#include <qmath.h>
+#include <qnumeric.h>
+#include <cassert>
+#include <time.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 <qv4isel_masm_p.h>
+
+#include <wtf/MathExtras.h>
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# ifndef Q_OS_VXWORKS
+# include <sys/time.h>
+# else
+# include "qplatformdefs.h"
+# endif
+#endif
+
+using namespace QV4;
+
+static const double HoursPerDay = 24.0;
+static const double MinutesPerHour = 60.0;
+static const double SecondsPerMinute = 60.0;
+static const double msPerSecond = 1000.0;
+static const double msPerMinute = 60000.0;
+static const double msPerHour = 3600000.0;
+static const double msPerDay = 86400000.0;
+
+static double LocalTZA = 0.0; // initialized at startup
+
+static inline double TimeWithinDay(double t)
+{
+ double r = ::fmod(t, msPerDay);
+ return (r >= 0) ? r : r + msPerDay;
+}
+
+static inline int HourFromTime(double t)
+{
+ int r = int(::fmod(::floor(t / msPerHour), HoursPerDay));
+ return (r >= 0) ? r : r + int(HoursPerDay);
+}
+
+static inline int MinFromTime(double t)
+{
+ int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour));
+ return (r >= 0) ? r : r + int(MinutesPerHour);
+}
+
+static inline int SecFromTime(double t)
+{
+ int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute));
+ return (r >= 0) ? r : r + int(SecondsPerMinute);
+}
+
+static inline int msFromTime(double t)
+{
+ int r = int(::fmod(t, msPerSecond));
+ return (r >= 0) ? r : r + int(msPerSecond);
+}
+
+static inline double Day(double t)
+{
+ return ::floor(t / msPerDay);
+}
+
+static inline double DaysInYear(double y)
+{
+ if (::fmod(y, 4))
+ return 365;
+
+ else if (::fmod(y, 100))
+ return 366;
+
+ else if (::fmod(y, 400))
+ return 365;
+
+ return 366;
+}
+
+static inline double DayFromYear(double y)
+{
+ return 365 * (y - 1970)
+ + ::floor((y - 1969) / 4)
+ - ::floor((y - 1901) / 100)
+ + ::floor((y - 1601) / 400);
+}
+
+static inline double TimeFromYear(double y)
+{
+ return msPerDay * DayFromYear(y);
+}
+
+static inline double YearFromTime(double t)
+{
+ int y = 1970;
+ y += (int) ::floor(t / (msPerDay * 365.2425));
+
+ double t2 = TimeFromYear(y);
+ return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y;
+}
+
+static inline bool InLeapYear(double t)
+{
+ double x = DaysInYear(YearFromTime(t));
+ if (x == 365)
+ return 0;
+
+ assert(x == 366);
+ return 1;
+}
+
+static inline double DayWithinYear(double t)
+{
+ return Day(t) - DayFromYear(YearFromTime(t));
+}
+
+static inline double MonthFromTime(double t)
+{
+ double d = DayWithinYear(t);
+ double l = InLeapYear(t);
+
+ if (d < 31.0)
+ return 0;
+
+ else if (d < 59.0 + l)
+ return 1;
+
+ else if (d < 90.0 + l)
+ return 2;
+
+ else if (d < 120.0 + l)
+ return 3;
+
+ else if (d < 151.0 + l)
+ return 4;
+
+ else if (d < 181.0 + l)
+ return 5;
+
+ else if (d < 212.0 + l)
+ return 6;
+
+ else if (d < 243.0 + l)
+ return 7;
+
+ else if (d < 273.0 + l)
+ return 8;
+
+ else if (d < 304.0 + l)
+ return 9;
+
+ else if (d < 334.0 + l)
+ return 10;
+
+ else if (d < 365.0 + l)
+ return 11;
+
+ return qSNaN(); // ### assert?
+}
+
+static inline double DateFromTime(double t)
+{
+ int m = (int) Value::toInteger(MonthFromTime(t));
+ double d = DayWithinYear(t);
+ double l = InLeapYear(t);
+
+ switch (m) {
+ case 0: return d + 1.0;
+ case 1: return d - 30.0;
+ case 2: return d - 58.0 - l;
+ case 3: return d - 89.0 - l;
+ case 4: return d - 119.0 - l;
+ case 5: return d - 150.0 - l;
+ case 6: return d - 180.0 - l;
+ case 7: return d - 211.0 - l;
+ case 8: return d - 242.0 - l;
+ case 9: return d - 272.0 - l;
+ case 10: return d - 303.0 - l;
+ case 11: return d - 333.0 - l;
+ }
+
+ return qSNaN(); // ### assert
+}
+
+static inline double WeekDay(double t)
+{
+ double r = ::fmod (Day(t) + 4.0, 7.0);
+ return (r >= 0) ? r : r + 7.0;
+}
+
+
+static inline double MakeTime(double hour, double min, double sec, double ms)
+{
+ return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
+}
+
+static inline double DayFromMonth(double month, double leap)
+{
+ switch ((int) month) {
+ case 0: return 0;
+ case 1: return 31.0;
+ case 2: return 59.0 + leap;
+ case 3: return 90.0 + leap;
+ case 4: return 120.0 + leap;
+ case 5: return 151.0 + leap;
+ case 6: return 181.0 + leap;
+ case 7: return 212.0 + leap;
+ case 8: return 243.0 + leap;
+ case 9: return 273.0 + leap;
+ case 10: return 304.0 + leap;
+ case 11: return 334.0 + leap;
+ }
+
+ return qSNaN(); // ### assert?
+}
+
+static double MakeDay(double year, double month, double day)
+{
+ year += ::floor(month / 12.0);
+
+ month = ::fmod(month, 12.0);
+ if (month < 0)
+ month += 12.0;
+
+ double d = DayFromYear(year);
+ bool leap = InLeapYear(d*msPerDay);
+
+ d += DayFromMonth(month, leap);
+ d += day - 1;
+
+ return d;
+}
+
+static inline double MakeDate(double day, double time)
+{
+ return day * msPerDay + time;
+}
+
+static inline double DaylightSavingTA(double t)
+{
+ struct tm tmtm;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ __time64_t tt = (__time64_t)(t / msPerSecond);
+ // _localtime_64_s returns non-zero on failure
+ if (_localtime64_s(&tmtm, &tt) != 0)
+#else
+ long int tt = (long int)(t / msPerSecond);
+ if (!localtime_r((const time_t*) &tt, &tmtm))
+#endif
+ return 0;
+ return (tmtm.tm_isdst > 0) ? msPerHour : 0;
+}
+
+static inline double LocalTime(double t)
+{
+ return t + LocalTZA + DaylightSavingTA(t);
+}
+
+static inline double UTC(double t)
+{
+ return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+}
+
+static inline double currentTime()
+{
+#ifndef Q_OS_WIN
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+ return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0));
+#else
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ FILETIME ft;
+ SystemTimeToFileTime(&st, &ft);
+ LARGE_INTEGER li;
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0;
+#endif
+}
+
+static inline double TimeClip(double t)
+{
+ if (! qIsFinite(t) || fabs(t) > 8.64e15)
+ return qSNaN();
+ return Value::toInteger(t);
+}
+
+static inline double ParseString(const QString &s)
+{
+ // first try the format defined in 15.9.1.15, only if that fails fall back to
+ // QDateTime for parsing
+
+ // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ
+ // It can be date or time only, and the second and later components
+ // of both fields are optional
+ // and extended syntax for negative and large positive years exists: +/-YYYYYY
+
+ enum Format {
+ Year,
+ Month,
+ Day,
+ Hour,
+ Minute,
+ Second,
+ MilliSecond,
+ TimezoneHour,
+ TimezoneMinute,
+ Done
+ };
+
+ const QChar *ch = s.constData();
+ const QChar *end = ch + s.length();
+
+ uint format = Year;
+ int current = 0;
+ int currentSize = 0;
+ bool extendedYear = false;
+
+ int yearSign = 1;
+ int year = 0;
+ int month = 0;
+ int day = 1;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ int msec = 0;
+ int offsetSign = 1;
+ int offset = 0;
+
+ bool error = false;
+ if (*ch == '+' || *ch == '-') {
+ extendedYear = true;
+ if (*ch == '-')
+ yearSign = -1;
+ ++ch;
+ }
+ while (ch <= end) {
+ if (*ch >= '0' && *ch <= '9') {
+ current *= 10;
+ current += ch->unicode() - '0';
+ ++currentSize;
+ } else { // other char, delimits field
+ switch (format) {
+ case Year:
+ year = current;
+ if (extendedYear)
+ error = (currentSize != 6);
+ else
+ error = (currentSize != 4);
+ break;
+ case Month:
+ month = current - 1;
+ error = (currentSize != 2) || month > 11;
+ break;
+ case Day:
+ day = current;
+ error = (currentSize != 2) || day > 31;
+ break;
+ case Hour:
+ hour = current;
+ error = (currentSize != 2) || hour > 24;
+ break;
+ case Minute:
+ minute = current;
+ error = (currentSize != 2) || minute > 60;
+ break;
+ case Second:
+ second = current;
+ error = (currentSize != 2) || second > 60;
+ break;
+ case MilliSecond:
+ msec = current;
+ error = (currentSize != 3);
+ break;
+ case TimezoneHour:
+ offset = current*60;
+ error = (currentSize != 2) || offset > 23*60;
+ break;
+ case TimezoneMinute:
+ offset += current;
+ error = (currentSize != 2) || current >= 60;
+ break;
+ }
+ if (*ch == 'T') {
+ if (format >= Hour)
+ error = true;
+ format = Hour;
+ } else if (*ch == '-') {
+ if (format < Day)
+ ++format;
+ else if (format < Minute)
+ error = true;
+ else if (format >= TimezoneHour)
+ error = true;
+ else {
+ offsetSign = -1;
+ format = TimezoneHour;
+ }
+ } else if (*ch == ':') {
+ if (format != Hour && format != Minute && format != TimezoneHour)
+ error = true;
+ ++format;
+ } else if (*ch == '.') {
+ if (format != Second)
+ error = true;
+ ++format;
+ } else if (*ch == '+') {
+ if (format < Minute || format >= TimezoneHour)
+ error = true;
+ format = TimezoneHour;
+ } else if (*ch == 'Z' || *ch == 0) {
+ format = Done;
+ }
+ current = 0;
+ currentSize = 0;
+ }
+ if (error || format == Done)
+ break;
+ ++ch;
+ }
+
+ if (!error) {
+ double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec));
+ t -= offset * offsetSign * 60 * 1000;
+ return t;
+ }
+
+ QDateTime dt = QDateTime::fromString(s, Qt::TextDate);
+ if (!dt.isValid())
+ dt = QDateTime::fromString(s, Qt::ISODate);
+ if (!dt.isValid()) {
+ QStringList formats;
+ formats << QStringLiteral("M/d/yyyy")
+ << QStringLiteral("M/d/yyyy hh:mm")
+ << QStringLiteral("M/d/yyyy hh:mm A")
+
+ << QStringLiteral("M/d/yyyy, hh:mm")
+ << QStringLiteral("M/d/yyyy, hh:mm A")
+
+ << QStringLiteral("MMM d yyyy")
+ << QStringLiteral("MMM d yyyy hh:mm")
+ << QStringLiteral("MMM d yyyy hh:mm:ss")
+ << QStringLiteral("MMM d yyyy, hh:mm")
+ << QStringLiteral("MMM d yyyy, hh:mm:ss")
+
+ << QStringLiteral("MMMM d yyyy")
+ << QStringLiteral("MMMM d yyyy hh:mm")
+ << QStringLiteral("MMMM d yyyy hh:mm:ss")
+ << QStringLiteral("MMMM d yyyy, hh:mm")
+ << QStringLiteral("MMMM d yyyy, hh:mm:ss")
+
+ << QStringLiteral("MMM d, yyyy")
+ << QStringLiteral("MMM d, yyyy hh:mm")
+ << QStringLiteral("MMM d, yyyy hh:mm:ss")
+
+ << QStringLiteral("MMMM d, yyyy")
+ << QStringLiteral("MMMM d, yyyy hh:mm")
+ << QStringLiteral("MMMM d, yyyy hh:mm:ss")
+
+ << QStringLiteral("d MMM yyyy")
+ << QStringLiteral("d MMM yyyy hh:mm")
+ << QStringLiteral("d MMM yyyy hh:mm:ss")
+ << QStringLiteral("d MMM yyyy, hh:mm")
+ << QStringLiteral("d MMM yyyy, hh:mm:ss")
+
+ << QStringLiteral("d MMMM yyyy")
+ << QStringLiteral("d MMMM yyyy hh:mm")
+ << QStringLiteral("d MMMM yyyy hh:mm:ss")
+ << QStringLiteral("d MMMM yyyy, hh:mm")
+ << QStringLiteral("d MMMM yyyy, hh:mm:ss")
+
+ << QStringLiteral("d MMM, yyyy")
+ << QStringLiteral("d MMM, yyyy hh:mm")
+ << QStringLiteral("d MMM, yyyy hh:mm:ss")
+
+ << QStringLiteral("d MMMM, yyyy")
+ << QStringLiteral("d MMMM, yyyy hh:mm")
+ << QStringLiteral("d MMMM, yyyy hh:mm:ss");
+
+ for (int i = 0; i < formats.size(); ++i) {
+ dt = QDateTime::fromString(s, formats.at(i));
+ if (dt.isValid())
+ break;
+ }
+ }
+ if (!dt.isValid())
+ return qSNaN();
+ return dt.toMSecsSinceEpoch();
+}
+
+/*!
+ \internal
+
+ Converts the ECMA Date value \tt (in UTC form) to QDateTime
+ according to \a spec.
+*/
+static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec)
+{
+ if (std::isnan(t))
+ return QDateTime();
+ if (spec == Qt::LocalTime)
+ t = LocalTime(t);
+ int year = int(YearFromTime(t));
+ int month = int(MonthFromTime(t) + 1);
+ int day = int(DateFromTime(t));
+ int hours = HourFromTime(t);
+ int mins = MinFromTime(t);
+ int secs = SecFromTime(t);
+ int ms = msFromTime(t);
+ return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec);
+}
+
+static inline QString ToString(double t)
+{
+ if (std::isnan(t))
+ return QStringLiteral("Invalid Date");
+ QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT");
+ double tzoffset = LocalTZA + DaylightSavingTA(t);
+ if (tzoffset) {
+ int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
+ int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
+ str.append(QLatin1Char((tzoffset > 0) ? '+' : '-'));
+ if (hours < 10)
+ str.append(QLatin1Char('0'));
+ str.append(QString::number(hours));
+ if (mins < 10)
+ str.append(QLatin1Char('0'));
+ str.append(QString::number(mins));
+ }
+ return str;
+}
+
+static inline QString ToUTCString(double t)
+{
+ if (std::isnan(t))
+ return QStringLiteral("Invalid Date");
+ return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT");
+}
+
+static inline QString ToDateString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).date().toString();
+}
+
+static inline QString ToTimeString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).time().toString();
+}
+
+static inline QString ToLocaleString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate);
+}
+
+static inline QString ToLocaleDateString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate);
+}
+
+static inline QString ToLocaleTimeString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate);
+}
+
+static double getLocalTZA()
+{
+#ifndef Q_OS_WIN
+ struct tm t;
+ time_t curr;
+ time(&curr);
+ localtime_r(&curr, &t);
+ time_t locl = mktime(&t);
+ gmtime_r(&curr, &t);
+ time_t globl = mktime(&t);
+ return double(locl - globl) * 1000.0;
+#else
+ TIME_ZONE_INFORMATION tzInfo;
+ GetTimeZoneInformation(&tzInfo);
+ return -tzInfo.Bias * 60.0 * 1000.0;
+#endif
+}
+
+DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date)
+ : Object(engine)
+{
+ type = Type_DateObject;
+ value = Value::fromDouble(date.toMSecsSinceEpoch());
+}
+
+QDateTime DateObject::toQDateTime() const
+{
+ return ToDateTime(value.asDouble(), Qt::LocalTime);
+}
+
+DEFINE_MANAGED_VTABLE(DateCtor);
+
+DateCtor::DateCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Date")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value DateCtor::construct(Managed *m, Value *args, int argc)
+{
+ double t = 0;
+
+ if (argc == 0)
+ t = currentTime();
+
+ else if (argc == 1) {
+ Value arg = args[0];
+ if (DateObject *d = arg.asDateObject())
+ arg = d->value;
+ else
+ arg = __qmljs_to_primitive(arg, PREFERREDTYPE_HINT);
+
+ if (arg.isString())
+ t = ParseString(arg.stringValue()->toQString());
+ else
+ t = TimeClip(arg.toNumber());
+ }
+
+ else { // argc > 1
+ double year = args[0].toNumber();
+ double month = args[1].toNumber();
+ double day = argc >= 3 ? args[2].toNumber() : 1;
+ double hours = argc >= 4 ? args[3].toNumber() : 0;
+ double mins = argc >= 5 ? args[4].toNumber() : 0;
+ double secs = argc >= 6 ? args[5].toNumber() : 0;
+ double ms = argc >= 7 ? args[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));
+ }
+
+ Object *d = m->engine()->newDateObject(Value::fromDouble(t));
+ return Value::fromObject(d);
+}
+
+Value DateCtor::call(Managed *m, const Value &, Value *, int)
+{
+ double t = currentTime();
+ return Value::fromString(m->engine()->current, ToString(t));
+}
+
+void DatePrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7));
+ LocalTZA = getLocalTZA();
+
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0);
+
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0);
+ defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0);
+ defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2);
+ defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3);
+ defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4);
+ defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2);
+ defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1);
+ defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3);
+ defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3);
+ defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1);
+}
+
+double DatePrototype::getThisDate(ExecutionContext *ctx)
+{
+ if (DateObject *thisObject = ctx->thisObject.asDateObject())
+ return thisObject->value.asDouble();
+ else {
+ ctx->throwTypeError();
+ return 0;
+ }
+}
+
+Value DatePrototype::method_parse(SimpleCallContext *ctx)
+{
+ return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString()));
+}
+
+Value DatePrototype::method_UTC(SimpleCallContext *ctx)
+{
+ const int numArgs = ctx->argumentCount;
+ if (numArgs >= 2) {
+ double year = ctx->argument(0).toNumber();
+ double month = ctx->argument(1).toNumber();
+ double day = numArgs >= 3 ? ctx->argument(2).toNumber() : 1;
+ double hours = numArgs >= 4 ? ctx->argument(3).toNumber() : 0;
+ double mins = numArgs >= 5 ? ctx->argument(4).toNumber() : 0;
+ double secs = numArgs >= 6 ? ctx->argument(5).toNumber() : 0;
+ double ms = numArgs >= 7 ? ctx->argument(6).toNumber() : 0;
+ if (year >= 0 && year <= 99)
+ year += 1900;
+ double t = MakeDate(MakeDay(year, month, day),
+ MakeTime(hours, mins, secs, ms));
+ return Value::fromDouble(TimeClip(t));
+ }
+ return Value::undefinedValue();
+}
+
+Value DatePrototype::method_now(SimpleCallContext *ctx)
+{
+ Q_UNUSED(ctx);
+ double t = currentTime();
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_toString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToString(t));
+}
+
+Value DatePrototype::method_toDateString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToDateString(t));
+}
+
+Value DatePrototype::method_toTimeString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToTimeString(t));
+}
+
+Value DatePrototype::method_toLocaleString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleString(t));
+}
+
+Value DatePrototype::method_toLocaleDateString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleDateString(t));
+}
+
+Value DatePrototype::method_toLocaleTimeString(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleTimeString(t));
+}
+
+Value DatePrototype::method_valueOf(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getTime(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getYear(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(LocalTime(t)) - 1900;
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getFullYear(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMonth(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MonthFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MonthFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDate(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = DateFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = DateFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDay(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = WeekDay(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = WeekDay(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getHours(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = HourFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = HourFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMinutes(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MinFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MinFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getSeconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = SecFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = SecFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = msFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = msFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = (t - LocalTime(t)) / msPerMinute;
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_setTime(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ self->value.setDouble(TimeClip(ctx->argument(0).toNumber()));
+ return self->value;
+}
+
+Value DatePrototype::method_setMilliseconds(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double ms = ctx->argument(0).toNumber();
+ self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMilliseconds(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double ms = ctx->argument(0).toNumber();
+ self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ return self->value;
+}
+
+Value DatePrototype::method_setSeconds(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double sec = ctx->argument(0).toNumber();
+ double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCSeconds(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double sec = ctx->argument(0).toNumber();
+ double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setMinutes(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double min = ctx->argument(0).toNumber();
+ double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber();
+ double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMinutes(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double min = ctx->argument(0).toNumber();
+ double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber();
+ double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setHours(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double hour = ctx->argument(0).toNumber();
+ double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber();
+ double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber();
+ double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCHours(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double hour = ctx->argument(0).toNumber();
+ double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber();
+ double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber();
+ double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber();
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setDate(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double date = ctx->argument(0).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCDate(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double date = ctx->argument(0).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setMonth(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double month = ctx->argument(0).toNumber();
+ double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMonth(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double month = ctx->argument(0).toNumber();
+ double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setYear(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ if (std::isnan(t))
+ t = 0;
+ else
+ t = LocalTime(t);
+ double year = ctx->argument(0).toNumber();
+ double r;
+ if (std::isnan(year)) {
+ r = qSNaN();
+ } else {
+ if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99))
+ year += 1900;
+ r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
+ r = UTC(MakeDate(r, TimeWithinDay(t)));
+ r = TimeClip(r);
+ }
+ self->value.setDouble(r);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCFullYear(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double year = ctx->argument(0).toNumber();
+ double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber();
+ double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setFullYear(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ if (std::isnan(t))
+ t = 0;
+ double year = ctx->argument(0).toNumber();
+ double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber();
+ double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber();
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_toUTCString(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ return Value::fromString(ctx, ToUTCString(t));
+}
+
+static void addZeroPrefixedInt(QString &str, int num, int nDigits)
+{
+ str.resize(str.size() + nDigits);
+
+ QChar *c = str.data() + str.size() - 1;
+ while (nDigits) {
+ *c = QChar(num % 10 + '0');
+ num /= 10;
+ --c;
+ --nDigits;
+ }
+}
+
+Value DatePrototype::method_toISOString(SimpleCallContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ if (!std::isfinite(t))
+ ctx->throwRangeError(ctx->thisObject);
+
+ QString result;
+ int year = (int)YearFromTime(t);
+ if (year < 0 || year > 9999) {
+ if (qAbs(year) >= 1000000)
+ return Value::fromString(ctx, QStringLiteral("Invalid Date"));
+ result += year < 0 ? '-' : '+';
+ year = qAbs(year);
+ addZeroPrefixedInt(result, year, 6);
+ } else {
+ addZeroPrefixedInt(result, year, 4);
+ }
+ result += '-';
+ addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2);
+ result += '-';
+ addZeroPrefixedInt(result, (int)DateFromTime(t), 2);
+ result += 'T';
+ addZeroPrefixedInt(result, HourFromTime(t), 2);
+ result += ':';
+ addZeroPrefixedInt(result, MinFromTime(t), 2);
+ result += ':';
+ addZeroPrefixedInt(result, SecFromTime(t), 2);
+ result += '.';
+ addZeroPrefixedInt(result, msFromTime(t), 3);
+ result += 'Z';
+
+ return Value::fromString(ctx, result);
+}
+
+Value DatePrototype::method_toJSON(SimpleCallContext *ctx)
+{
+ Value O = __qmljs_to_object(ctx, ctx->thisObject);
+ Value tv = __qmljs_to_primitive(O, NUMBER_HINT);
+
+ if (tv.isNumber() && !std::isfinite(tv.toNumber()))
+ return Value::nullValue();
+
+ FunctionObject *toIso = O.objectValue()->get(ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject();
+
+ if (!toIso)
+ ctx->throwTypeError();
+
+ return toIso->call(ctx->thisObject, 0, 0);
+}
+
+void DatePrototype::timezoneUpdated()
+{
+ LocalTZA = getLocalTZA();
+}
diff --git a/src/qml/qml/v4/qv4dateobject_p.h b/src/qml/qml/v4/qv4dateobject_p.h
new file mode 100644
index 0000000000..4e833e143f
--- /dev/null
+++ b/src/qml/qml/v4/qv4dateobject_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4DATEOBJECT_P_H
+#define QV4DATEOBJECT_P_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDateTime;
+
+namespace QV4 {
+
+struct DateObject: Object {
+ Value value;
+ DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; }
+ DateObject(ExecutionEngine *engine, const QDateTime &value);
+
+ QDateTime toQDateTime() const;
+};
+
+struct DateCtor: FunctionObject
+{
+ DateCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct DatePrototype: DateObject
+{
+ DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static double getThisDate(ExecutionContext *ctx);
+
+ static Value method_parse(SimpleCallContext *ctx);
+ static Value method_UTC(SimpleCallContext *ctx);
+ static Value method_now(SimpleCallContext *ctx);
+
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_toDateString(SimpleCallContext *ctx);
+ static Value method_toTimeString(SimpleCallContext *ctx);
+ static Value method_toLocaleString(SimpleCallContext *ctx);
+ static Value method_toLocaleDateString(SimpleCallContext *ctx);
+ static Value method_toLocaleTimeString(SimpleCallContext *ctx);
+ static Value method_valueOf(SimpleCallContext *ctx);
+ static Value method_getTime(SimpleCallContext *ctx);
+ static Value method_getYear(SimpleCallContext *ctx);
+ static Value method_getFullYear(SimpleCallContext *ctx);
+ static Value method_getUTCFullYear(SimpleCallContext *ctx);
+ static Value method_getMonth(SimpleCallContext *ctx);
+ static Value method_getUTCMonth(SimpleCallContext *ctx);
+ static Value method_getDate(SimpleCallContext *ctx);
+ static Value method_getUTCDate(SimpleCallContext *ctx);
+ static Value method_getDay(SimpleCallContext *ctx);
+ static Value method_getUTCDay(SimpleCallContext *ctx);
+ static Value method_getHours(SimpleCallContext *ctx);
+ static Value method_getUTCHours(SimpleCallContext *ctx);
+ static Value method_getMinutes(SimpleCallContext *ctx);
+ static Value method_getUTCMinutes(SimpleCallContext *ctx);
+ static Value method_getSeconds(SimpleCallContext *ctx);
+ static Value method_getUTCSeconds(SimpleCallContext *ctx);
+ static Value method_getMilliseconds(SimpleCallContext *ctx);
+ static Value method_getUTCMilliseconds(SimpleCallContext *ctx);
+ static Value method_getTimezoneOffset(SimpleCallContext *ctx);
+ static Value method_setTime(SimpleCallContext *ctx);
+ static Value method_setMilliseconds(SimpleCallContext *ctx);
+ static Value method_setUTCMilliseconds(SimpleCallContext *ctx);
+ static Value method_setSeconds(SimpleCallContext *ctx);
+ static Value method_setUTCSeconds(SimpleCallContext *ctx);
+ static Value method_setMinutes(SimpleCallContext *ctx);
+ static Value method_setUTCMinutes(SimpleCallContext *ctx);
+ static Value method_setHours(SimpleCallContext *ctx);
+ static Value method_setUTCHours(SimpleCallContext *ctx);
+ static Value method_setDate(SimpleCallContext *ctx);
+ static Value method_setUTCDate(SimpleCallContext *ctx);
+ static Value method_setMonth(SimpleCallContext *ctx);
+ static Value method_setUTCMonth(SimpleCallContext *ctx);
+ static Value method_setYear(SimpleCallContext *ctx);
+ static Value method_setFullYear(SimpleCallContext *ctx);
+ static Value method_setUTCFullYear(SimpleCallContext *ctx);
+ static Value method_toUTCString(SimpleCallContext *ctx);
+ static Value method_toISOString(SimpleCallContext *ctx);
+ static Value method_toJSON(SimpleCallContext *ctx);
+
+ static void timezoneUpdated();
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4debugging.cpp b/src/qml/qml/v4/qv4debugging.cpp
new file mode 100644
index 0000000000..624390d8f2
--- /dev/null
+++ b/src/qml/qml/v4/qv4debugging.cpp
@@ -0,0 +1,372 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4debugging_p.h"
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+#include "moth/qv4instr_moth_p.h"
+#include <iostream>
+
+using namespace QV4;
+using namespace QV4::Debugging;
+
+Debugger::Debugger(QV4::ExecutionEngine *engine)
+ : _engine(engine)
+ , m_agent(0)
+ , m_state(Running)
+ , m_pauseRequested(false)
+ , m_currentInstructionPointer(0)
+{
+ qMetaTypeId<Debugger*>();
+}
+
+Debugger::~Debugger()
+{
+ detachFromAgent();
+}
+
+void Debugger::attachToAgent(DebuggerAgent *agent)
+{
+ Q_ASSERT(!m_agent);
+ m_agent = agent;
+}
+
+void Debugger::detachFromAgent()
+{
+ DebuggerAgent *agent = 0;
+ {
+ QMutexLocker locker(&m_lock);
+ agent = m_agent;
+ m_agent = 0;
+ }
+ if (agent)
+ agent->removeDebugger(this);
+}
+
+void Debugger::pause()
+{
+ QMutexLocker locker(&m_lock);
+ if (m_state == Paused)
+ return;
+ m_pauseRequested = true;
+}
+
+void Debugger::resume()
+{
+ QMutexLocker locker(&m_lock);
+ Q_ASSERT(m_state == Paused);
+ m_runningCondition.wakeAll();
+}
+
+void Debugger::addBreakPoint(const QString &fileName, int lineNumber)
+{
+ QMutexLocker locker(&m_lock);
+ if (!m_pendingBreakPointsToRemove.remove(fileName, lineNumber))
+ m_pendingBreakPointsToAdd.add(fileName, lineNumber);
+ m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty();
+}
+
+void Debugger::removeBreakPoint(const QString &fileName, int lineNumber)
+{
+ QMutexLocker locker(&m_lock);
+ if (!m_pendingBreakPointsToAdd.remove(fileName, lineNumber))
+ m_pendingBreakPointsToRemove.add(fileName, lineNumber);
+ m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty();
+}
+
+Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) const
+{
+ if (!code)
+ code = m_currentInstructionPointer;
+ // ### Locking
+ ExecutionState state;
+
+ QV4::ExecutionContext *context = _engine->current;
+ QV4::Function *function = 0;
+ if (CallContext *callCtx = context->asCallContext())
+ function = callCtx->function->function;
+ else {
+ Q_ASSERT(context->type == QV4::ExecutionContext::Type_GlobalContext);
+ function = context->engine->globalCode;
+ }
+
+ state.function = function;
+ state.fileName = function->sourceFile;
+
+ qptrdiff relativeProgramCounter = code - function->codeData;
+ state.lineNumber = function->lineNumberForProgramCounter(relativeProgramCounter);
+
+ return state;
+}
+
+void Debugger::setPendingBreakpoints(Function *function)
+{
+ m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false);
+}
+
+void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit)
+{
+ QMutexLocker locker(&m_lock);
+ m_currentInstructionPointer = code;
+
+ // Do debugger internal work
+ if (m_havePendingBreakPoints) {
+
+ if (breakPointHit) {
+ ExecutionState state = currentExecutionState();
+ breakPointHit = !m_pendingBreakPointsToRemove.contains(state.fileName, state.lineNumber);
+ }
+
+ applyPendingBreakPoints();
+ }
+
+ // Serve debugging requests from the agent
+ if (m_pauseRequested) {
+ m_pauseRequested = false;
+ pauseAndWait();
+ } else if (breakPointHit)
+ pauseAndWait();
+
+ if (!m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty())
+ applyPendingBreakPoints();
+}
+
+void Debugger::aboutToThrow(const QV4::Value &value)
+{
+ qDebug() << "*** We are about to throw...";
+}
+
+void Debugger::pauseAndWait()
+{
+ m_state = Paused;
+ QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, Q_ARG(QV4::Debugging::Debugger*, this));
+ m_runningCondition.wait(&m_lock);
+ m_state = Running;
+}
+
+void Debugger::applyPendingBreakPoints()
+{
+ foreach (Function *function, _engine->functions) {
+ m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false);
+ m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true);
+ }
+
+ for (BreakPoints::ConstIterator it = m_pendingBreakPointsToAdd.constBegin(),
+ end = m_pendingBreakPointsToAdd.constEnd(); it != end; ++it) {
+ foreach (int lineNumber, it.value())
+ m_pendingBreakPointsToAddToFutureCode.add(it.key(), lineNumber);
+ }
+
+ m_pendingBreakPointsToAdd.clear();
+ m_pendingBreakPointsToRemove.clear();
+ m_havePendingBreakPoints = false;
+}
+
+static void realDumpValue(QV4::Value v, QV4::ExecutionContext *ctx, std::string prefix)
+{
+ using namespace QV4;
+ using namespace std;
+ cout << prefix << "tag: " << hex << v.tag << dec << endl << prefix << "\t-> ";
+ switch (v.type()) {
+ case Value::Undefined_Type: cout << "Undefined" << endl; return;
+ case Value::Null_Type: cout << "Null" << endl; return;
+ case Value::Boolean_Type: cout << "Boolean"; break;
+ case Value::Integer_Type: cout << "Integer"; break;
+ case Value::Object_Type: cout << "Object"; break;
+ case Value::String_Type: cout << "String"; break;
+ default: cout << "UNKNOWN" << endl; return;
+ }
+ cout << endl;
+
+ if (v.isBoolean()) {
+ cout << prefix << "\t-> " << (v.booleanValue() ? "TRUE" : "FALSE") << endl;
+ return;
+ }
+
+ if (v.isInteger()) {
+ cout << prefix << "\t-> " << v.integerValue() << endl;
+ return;
+ }
+
+ if (v.isDouble()) {
+ cout << prefix << "\t-> " << v.doubleValue() << endl;
+ return;
+ }
+
+ if (v.isString()) {
+ // maybe check something on the Managed object?
+ cout << prefix << "\t-> @" << hex << v.stringValue() << endl;
+ cout << prefix << "\t-> \"" << qPrintable(v.stringValue()->toQString()) << "\"" << endl;
+ return;
+ }
+
+ Object *o = v.objectValue();
+ if (!o)
+ return;
+
+ cout << prefix << "\t-> @" << hex << o << endl;
+ cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> ";
+ switch (o->internalType()) {
+ case QV4::Managed::Type_Invalid: cout << "Invalid"; break;
+ case QV4::Managed::Type_String: cout << "String"; break;
+ case QV4::Managed::Type_Object: cout << "Object"; break;
+ case QV4::Managed::Type_ArrayObject: cout << "ArrayObject"; break;
+ case QV4::Managed::Type_FunctionObject: cout << "FunctionObject"; break;
+ case QV4::Managed::Type_BooleanObject: cout << "BooleanObject"; break;
+ case QV4::Managed::Type_NumberObject: cout << "NumberObject"; break;
+ case QV4::Managed::Type_StringObject: cout << "StringObject"; break;
+ case QV4::Managed::Type_DateObject: cout << "DateObject"; break;
+ case QV4::Managed::Type_RegExpObject: cout << "RegExpObject"; break;
+ case QV4::Managed::Type_ErrorObject: cout << "ErrorObject"; break;
+ case QV4::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break;
+ case QV4::Managed::Type_JSONObject: cout << "JSONObject"; break;
+ case QV4::Managed::Type_MathObject: cout << "MathObject"; break;
+ case QV4::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break;
+ default: cout << "UNKNOWN" << endl; return;
+ }
+ cout << endl;
+
+ cout << prefix << "properties:" << endl;
+ ForEachIteratorObject it(ctx, o);
+ for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) {
+ cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl;
+ PropertyAttributes attrs;
+ Property *d = o->__getOwnProperty__(name.stringValue(), &attrs);
+ Value pval = o->getValue(d, attrs);
+ cout << prefix << "\tvalue:" << endl;
+ realDumpValue(pval, ctx, prefix + "\t");
+ }
+}
+
+void dumpValue(QV4::Value v, QV4::ExecutionContext *ctx)
+{
+ realDumpValue(v, ctx, std::string(""));
+}
+
+
+void DebuggerAgent::addDebugger(Debugger *debugger)
+{
+ Q_ASSERT(!m_debuggers.contains(debugger));
+ m_debuggers << debugger;
+ debugger->attachToAgent(this);
+}
+
+void DebuggerAgent::removeDebugger(Debugger *debugger)
+{
+ m_debuggers.removeAll(debugger);
+ debugger->detachFromAgent();
+}
+
+void DebuggerAgent::pause(Debugger *debugger)
+{
+ debugger->pause();
+}
+
+void DebuggerAgent::addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber)
+{
+ debugger->addBreakPoint(fileName, lineNumber);
+}
+
+void DebuggerAgent::removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber)
+{
+ debugger->removeBreakPoint(fileName, lineNumber);
+}
+
+DebuggerAgent::~DebuggerAgent()
+{
+ Q_ASSERT(m_debuggers.isEmpty());
+}
+
+void Debugger::BreakPoints::add(const QString &fileName, int lineNumber)
+{
+ QList<int> &lines = (*this)[fileName];
+ if (!lines.contains(lineNumber)) {
+ lines.append(lineNumber);
+ qSort(lines);
+ }
+}
+
+bool Debugger::BreakPoints::remove(const QString &fileName, int lineNumber)
+{
+ Iterator breakPoints = find(fileName);
+ if (breakPoints == constEnd())
+ return false;
+ return breakPoints->removeAll(lineNumber) > 0;
+}
+
+bool Debugger::BreakPoints::contains(const QString &fileName, int lineNumber) const
+{
+ ConstIterator breakPoints = find(fileName);
+ if (breakPoints == constEnd())
+ return false;
+ return breakPoints->contains(lineNumber);
+}
+
+void Debugger::BreakPoints::applyToFunction(Function *function, bool removeBreakPoints)
+{
+ Iterator breakPointsForFile = find(function->sourceFile);
+ if (breakPointsForFile == end())
+ return;
+
+ QList<int>::Iterator breakPoint = breakPointsForFile->begin();
+ while (breakPoint != breakPointsForFile->end()) {
+ bool breakPointFound = false;
+ for (QVector<LineNumberMapping>::ConstIterator mapping = function->lineNumberMappings.constBegin(),
+ end = function->lineNumberMappings.constEnd(); mapping != end; ++mapping) {
+ if (mapping->lineNumber == *breakPoint) {
+ uchar *codePtr = const_cast<uchar *>(function->codeData) + mapping->codeOffset;
+ QQmlJS::Moth::Instr *instruction = reinterpret_cast<QQmlJS::Moth::Instr*>(codePtr);
+ instruction->common.breakPoint = !removeBreakPoints;
+ // Continue setting the next break point.
+ breakPointFound = true;
+ break;
+ }
+ }
+ if (breakPointFound)
+ breakPoint = breakPointsForFile->erase(breakPoint);
+ else
+ ++breakPoint;
+ }
+
+ if (breakPointsForFile->isEmpty())
+ erase(breakPointsForFile);
+}
diff --git a/src/qml/qml/v4/qv4debugging_p.h b/src/qml/qml/v4/qv4debugging_p.h
new file mode 100644
index 0000000000..4a273be732
--- /dev/null
+++ b/src/qml/qml/v4/qv4debugging_p.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGING_H
+#define DEBUGGING_H
+
+#include "qv4global_p.h"
+#include "qv4engine_p.h"
+#include "qv4context_p.h"
+#include "qv4jsir_p.h"
+
+#include <QHash>
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+
+namespace Debugging {
+
+class DebuggerAgent;
+
+class Q_QML_EXPORT Debugger
+{
+public:
+ enum State {
+ Running,
+ Paused
+ };
+
+ Debugger(ExecutionEngine *_engine);
+ ~Debugger();
+
+ void attachToAgent(DebuggerAgent *agent);
+ void detachFromAgent();
+
+ void pause();
+ void resume();
+
+ State state() const { return m_state; }
+
+ void addBreakPoint(const QString &fileName, int lineNumber);
+ void removeBreakPoint(const QString &fileName, int lineNumber);
+
+ struct ExecutionState
+ {
+ ExecutionState() : lineNumber(-1), function(0) {}
+ QString fileName;
+ int lineNumber;
+ Function *function;
+ };
+
+ ExecutionState currentExecutionState(const uchar *code = 0) const;
+
+ bool pauseAtNextOpportunity() const {
+ return m_pauseRequested || m_havePendingBreakPoints;
+ }
+ void setPendingBreakpoints(Function *function);
+
+public: // compile-time interface
+ void maybeBreakAtInstruction(const uchar *code, bool breakPointHit);
+
+public: // execution hooks
+ void aboutToThrow(const Value &value);
+
+private:
+ // requires lock to be held
+ void pauseAndWait();
+
+ void applyPendingBreakPoints();
+
+ struct BreakPoints : public QHash<QString, QList<int> >
+ {
+ void add(const QString &fileName, int lineNumber);
+ bool remove(const QString &fileName, int lineNumber);
+ bool contains(const QString &fileName, int lineNumber) const;
+ void applyToFunction(Function *function, bool removeBreakPoints);
+ };
+
+ QV4::ExecutionEngine *_engine;
+ DebuggerAgent *m_agent;
+ QMutex m_lock;
+ QWaitCondition m_runningCondition;
+ State m_state;
+ bool m_pauseRequested;
+ bool m_havePendingBreakPoints;
+ BreakPoints m_pendingBreakPointsToAdd;
+ BreakPoints m_pendingBreakPointsToAddToFutureCode;
+ BreakPoints m_pendingBreakPointsToRemove;
+ const uchar *m_currentInstructionPointer;
+};
+
+class Q_QML_EXPORT DebuggerAgent : public QObject
+{
+ Q_OBJECT
+public:
+ ~DebuggerAgent();
+
+ void addDebugger(Debugger *debugger);
+ void removeDebugger(Debugger *debugger);
+
+ void pause(Debugger *debugger);
+ void addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber);
+ void removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber);
+
+ Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger) = 0;
+
+protected:
+ QList<Debugger *> m_debuggers;
+};
+
+} // namespace Debugging
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QV4::Debugging::Debugger*)
+
+#endif // DEBUGGING_H
diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp
new file mode 100644
index 0000000000..0b7c850aa6
--- /dev/null
+++ b/src/qml/qml/v4/qv4engine.cpp
@@ -0,0 +1,837 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4engine_p.h>
+#include <qv4value_p.h>
+#include <qv4object_p.h>
+#include <qv4objectproto_p.h>
+#include <qv4arrayobject_p.h>
+#include <qv4booleanobject_p.h>
+#include <qv4globalobject_p.h>
+#include <qv4errorobject_p.h>
+#include <qv4functionobject_p.h>
+#include "qv4function_p.h"
+#include <qv4mathobject_p.h>
+#include <qv4numberobject_p.h>
+#include <qv4regexpobject_p.h>
+#include <qv4variantobject_p.h>
+#include <qv4runtime_p.h>
+#include "qv4mm_p.h"
+#include <qv4argumentsobject_p.h>
+#include <qv4dateobject_p.h>
+#include <qv4jsonobject_p.h>
+#include <qv4stringobject_p.h>
+#include <qv4identifiertable_p.h>
+#include <qv4unwindhelper_p.h>
+#include "qv4debugging_p.h"
+#include "qv4executableallocator_p.h"
+#include "qv4sequenceobject_p.h"
+#include "qv4qobjectwrapper_p.h"
+#include "qv4qmlextensions_p.h"
+#include "qv4stacktrace_p.h"
+
+#ifdef V4_ENABLE_JIT
+#include "qv4isel_masm_p.h"
+#endif // V4_ENABLE_JIT
+
+#include "qv4isel_moth_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory)
+ : memoryManager(new QV4::MemoryManager)
+ , executableAllocator(new QV4::ExecutableAllocator)
+ , regExpAllocator(new QV4::ExecutableAllocator)
+ , bumperPointerAllocator(new WTF::BumpPointerAllocator)
+ , debugger(0)
+ , globalObject(0)
+ , globalCode(0)
+ , functionsNeedSort(false)
+ , m_engineId(engineSerial.fetchAndAddOrdered(1))
+ , regExpCache(0)
+ , m_multiplyWrappedQObjects(0)
+ , m_qmlExtensions(0)
+{
+ MemoryManager::GCBlocker gcBlocker(memoryManager);
+
+ if (!factory) {
+#ifdef V4_ENABLE_JIT
+ factory = new QQmlJS::MASM::ISelFactory;
+#else // !V4_ENABLE_JIT
+ factory = new QQmlJS::Moth::ISelFactory;
+#endif // V4_ENABLE_JIT
+ }
+ iselFactory.reset(factory);
+
+ memoryManager->setExecutionEngine(this);
+
+ identifierTable = new IdentifierTable(this);
+
+ emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this);
+
+ id_undefined = newIdentifier(QStringLiteral("undefined"));
+ id_null = newIdentifier(QStringLiteral("null"));
+ id_true = newIdentifier(QStringLiteral("true"));
+ id_false = newIdentifier(QStringLiteral("false"));
+ id_boolean = newIdentifier(QStringLiteral("boolean"));
+ id_number = newIdentifier(QStringLiteral("number"));
+ id_string = newIdentifier(QStringLiteral("string"));
+ id_object = newIdentifier(QStringLiteral("object"));
+ id_function = newIdentifier(QStringLiteral("function"));
+ id_length = newIdentifier(QStringLiteral("length"));
+ id_prototype = newIdentifier(QStringLiteral("prototype"));
+ id_constructor = newIdentifier(QStringLiteral("constructor"));
+ id_arguments = newIdentifier(QStringLiteral("arguments"));
+ id_caller = newIdentifier(QStringLiteral("caller"));
+ id_this = newIdentifier(QStringLiteral("this"));
+ id___proto__ = newIdentifier(QStringLiteral("__proto__"));
+ id_enumerable = newIdentifier(QStringLiteral("enumerable"));
+ id_configurable = newIdentifier(QStringLiteral("configurable"));
+ id_writable = newIdentifier(QStringLiteral("writable"));
+ id_value = newIdentifier(QStringLiteral("value"));
+ id_get = newIdentifier(QStringLiteral("get"));
+ id_set = newIdentifier(QStringLiteral("set"));
+ id_eval = newIdentifier(QStringLiteral("eval"));
+ id_uintMax = newIdentifier(QStringLiteral("4294967295"));
+ id_name = newIdentifier(QStringLiteral("name"));
+
+ arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable);
+ initRootContext();
+
+ objectPrototype = new (memoryManager) ObjectPrototype(this);
+ stringPrototype = new (memoryManager) StringPrototype(this);
+ numberPrototype = new (memoryManager) NumberPrototype(this);
+ booleanPrototype = new (memoryManager) BooleanPrototype(this);
+ arrayPrototype = new (memoryManager) ArrayPrototype(rootContext);
+ datePrototype = new (memoryManager) DatePrototype(this);
+ functionPrototype = new (memoryManager) FunctionPrototype(rootContext);
+ regExpPrototype = new (memoryManager) RegExpPrototype(this);
+ errorPrototype = new (memoryManager) ErrorPrototype(this);
+ evalErrorPrototype = new (memoryManager) EvalErrorPrototype(this);
+ rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(this);
+ referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(this);
+ syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(this);
+ typeErrorPrototype = new (memoryManager) TypeErrorPrototype(this);
+ uRIErrorPrototype = new (memoryManager) URIErrorPrototype(this);
+
+ variantPrototype = new (memoryManager) VariantPrototype(this);
+ sequencePrototype = new (memoryManager) SequencePrototype(this);
+
+ stringPrototype->prototype = objectPrototype;
+ numberPrototype->prototype = objectPrototype;
+ booleanPrototype->prototype = objectPrototype;
+ arrayPrototype->prototype = objectPrototype;
+ datePrototype->prototype = objectPrototype;
+ functionPrototype->prototype = objectPrototype;
+ regExpPrototype->prototype = objectPrototype;
+ errorPrototype->prototype = objectPrototype;
+ evalErrorPrototype->prototype = objectPrototype;
+ rangeErrorPrototype->prototype = objectPrototype;
+ referenceErrorPrototype->prototype = objectPrototype;
+ syntaxErrorPrototype->prototype = objectPrototype;
+ typeErrorPrototype->prototype = objectPrototype;
+ uRIErrorPrototype->prototype = objectPrototype;
+
+ objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext));
+ stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext));
+ numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext));
+ booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext));
+ arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext));
+ functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext));
+ dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext));
+ regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext));
+ errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext));
+ evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext));
+ rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext));
+ referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext));
+ syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext));
+ typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext));
+ uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext));
+
+ objectCtor.objectValue()->prototype = functionPrototype;
+ stringCtor.objectValue()->prototype = functionPrototype;
+ numberCtor.objectValue()->prototype = functionPrototype;
+ booleanCtor.objectValue()->prototype = functionPrototype;
+ arrayCtor.objectValue()->prototype = functionPrototype;
+ functionCtor.objectValue()->prototype = functionPrototype;
+ dateCtor.objectValue()->prototype = functionPrototype;
+ regExpCtor.objectValue()->prototype = functionPrototype;
+ errorCtor.objectValue()->prototype = functionPrototype;
+ evalErrorCtor.objectValue()->prototype = functionPrototype;
+ rangeErrorCtor.objectValue()->prototype = functionPrototype;
+ referenceErrorCtor.objectValue()->prototype = functionPrototype;
+ syntaxErrorCtor.objectValue()->prototype = functionPrototype;
+ typeErrorCtor.objectValue()->prototype = functionPrototype;
+ uRIErrorCtor.objectValue()->prototype = functionPrototype;
+
+ objectPrototype->init(rootContext, objectCtor);
+ stringPrototype->init(this, stringCtor);
+ numberPrototype->init(rootContext, numberCtor);
+ booleanPrototype->init(rootContext, booleanCtor);
+ arrayPrototype->init(rootContext, arrayCtor);
+ datePrototype->init(rootContext, dateCtor);
+ functionPrototype->init(rootContext, functionCtor);
+ regExpPrototype->init(rootContext, regExpCtor);
+ errorPrototype->init(this, errorCtor);
+ evalErrorPrototype->init(this, evalErrorCtor);
+ rangeErrorPrototype->init(this, rangeErrorCtor);
+ referenceErrorPrototype->init(this, referenceErrorCtor);
+ syntaxErrorPrototype->init(this, syntaxErrorCtor);
+ typeErrorPrototype->init(this, typeErrorCtor);
+ uRIErrorPrototype->init(this, uRIErrorCtor);
+
+ variantPrototype->init(this);
+ sequencePrototype->init(this);
+
+ //
+ // set up the global object
+ //
+ globalObject = newObject(/*rootContext*/);
+ rootContext->global = globalObject;
+ rootContext->thisObject = Value::fromObject(globalObject);
+
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext)));
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext)));
+
+ globalObject->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue());
+ globalObject->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN()));
+ globalObject->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY));
+
+ evalFunction = new (memoryManager) EvalFunction(rootContext);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction));
+
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
+ globalObject->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
+}
+
+ExecutionEngine::~ExecutionEngine()
+{
+ delete debugger;
+ delete m_multiplyWrappedQObjects;
+ m_multiplyWrappedQObjects = 0;
+ delete memoryManager;
+ delete m_qmlExtensions;
+ emptyClass->destroy();
+ delete identifierTable;
+ delete bumperPointerAllocator;
+ delete regExpCache;
+ UnwindHelper::deregisterFunctions(functions);
+ qDeleteAll(functions);
+ delete regExpAllocator;
+ delete executableAllocator;
+}
+
+void ExecutionEngine::enableDebugger()
+{
+ Q_ASSERT(!debugger);
+ debugger = new Debugging::Debugger(this);
+ iselFactory.reset(new QQmlJS::Moth::ISelFactory);
+}
+
+void ExecutionEngine::initRootContext()
+{
+ rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
+ current = rootContext;
+ current->parent = 0;
+ rootContext->initGlobalContext(this);
+}
+
+InternalClass *ExecutionEngine::newClass(const InternalClass &other)
+{
+ return new (classPool.allocate(sizeof(InternalClass))) InternalClass(other);
+}
+
+WithContext *ExecutionEngine::newWithContext(Object *with)
+{
+ WithContext *w = static_cast<WithContext *>(memoryManager->allocContext(sizeof(WithContext)));
+ ExecutionContext *p = current;
+ current = w;
+ w->initWithContext(p, with);
+ return w;
+}
+
+CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue)
+{
+ CatchContext *c = static_cast<CatchContext *>(memoryManager->allocContext(sizeof(CatchContext)));
+ ExecutionContext *p = current;
+ current = c;
+ c->initCatchContext(p, exceptionVarName, exceptionValue);
+ return c;
+}
+
+CallContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc)
+{
+ CallContext *c = static_cast<CallContext *>(memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc)));
+ ExecutionContext *p = current;
+ current = c;
+ c->initCallContext(p, f, args, argc, thisObject);
+
+ return c;
+}
+
+CallContext *ExecutionEngine::newQmlContext(FunctionObject *f, Object *qml)
+{
+ CallContext *c = static_cast<CallContext *>(memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0)));
+
+ ExecutionContext *p = current;
+ current = c;
+ c->initQmlContext(p, qml, f);
+
+ return c;
+}
+
+CallContext *ExecutionEngine::newCallContext(void *stackSpace, FunctionObject *f, const Value &thisObject, Value *args, int argc)
+{
+ CallContext *c;
+ uint memory = requiredMemoryForExecutionContect(f, argc);
+ if (f->needsActivation || memory > stackContextSize) {
+ c = static_cast<CallContext *>(memoryManager->allocContext(memory));
+ } else {
+ c = (CallContext *)stackSpace;
+#ifndef QT_NO_DEBUG
+ c->next = (CallContext *)0x1;
+#endif
+ }
+
+ ExecutionContext *p = current;
+ current = c;
+ c->initCallContext(p, f, args, argc, thisObject);
+
+ return c;
+}
+
+
+ExecutionContext *ExecutionEngine::pushGlobalContext()
+{
+ GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
+ ExecutionContext *oldNext = g->next;
+ *g = *rootContext;
+ g->next = oldNext;
+ g->parent = current;
+ current = g;
+
+ return current;
+}
+
+Function *ExecutionEngine::newFunction(const QString &name)
+{
+ Function *f = new Function(newIdentifier(name));
+ functions.append(f);
+ functionsNeedSort = true;
+ return f;
+}
+
+FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *))
+{
+ BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code);
+ return f;
+}
+
+FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, Function *function)
+{
+ assert(function);
+
+ ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function);
+ return f;
+}
+
+BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs)
+{
+ assert(target);
+
+ BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs);
+ return f;
+}
+
+
+Object *ExecutionEngine::newObject()
+{
+ Object *object = new (memoryManager) Object(this);
+ object->prototype = objectPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newObject(InternalClass *internalClass)
+{
+ Object *object = new (memoryManager) Object(this, internalClass);
+ object->prototype = objectPrototype;
+ return object;
+}
+
+String *ExecutionEngine::newString(const QString &s)
+{
+ return new (memoryManager) String(this, s);
+}
+
+String *ExecutionEngine::newIdentifier(const QString &text)
+{
+ return identifierTable->insertString(text);
+}
+
+Object *ExecutionEngine::newStringObject(const Value &value)
+{
+ StringObject *object = new (memoryManager) StringObject(this, value);
+ object->prototype = stringPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newNumberObject(const Value &value)
+{
+ NumberObject *object = new (memoryManager) NumberObject(this, value);
+ object->prototype = numberPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newBooleanObject(const Value &value)
+{
+ Object *object = new (memoryManager) BooleanObject(this, value);
+ object->prototype = booleanPrototype;
+ return object;
+}
+
+ArrayObject *ExecutionEngine::newArrayObject(int count)
+{
+ ArrayObject *object = new (memoryManager) ArrayObject(this);
+ object->prototype = arrayPrototype;
+
+ if (count) {
+ if (count < 0x1000)
+ object->arrayReserve(count);
+ object->setArrayLengthUnchecked(count);
+ }
+ return object;
+}
+
+ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
+{
+ ArrayObject *object = new (memoryManager) ArrayObject(this, list);
+ object->prototype = arrayPrototype;
+ return object;
+}
+
+DateObject *ExecutionEngine::newDateObject(const Value &value)
+{
+ DateObject *object = new (memoryManager) DateObject(this, value);
+ object->prototype = datePrototype;
+ return object;
+}
+
+DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
+{
+ DateObject *object = new (memoryManager) DateObject(this, dt);
+ object->prototype = datePrototype;
+ return object;
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
+{
+ bool global = (flags & QQmlJS::V4IR::RegExp::RegExp_Global);
+ bool ignoreCase = false;
+ bool multiline = false;
+ if (flags & QQmlJS::V4IR::RegExp::RegExp_IgnoreCase)
+ ignoreCase = true;
+ if (flags & QQmlJS::V4IR::RegExp::RegExp_Multiline)
+ multiline = true;
+
+ return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global);
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(RegExp* re, bool global)
+{
+ RegExpObject *object = new (memoryManager) RegExpObject(this, re, global);
+ object->prototype = regExpPrototype;
+ return object;
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
+{
+ RegExpObject *object = new (memoryManager) RegExpObject(this, re);
+ object->prototype = regExpPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newErrorObject(const Value &value)
+{
+ ErrorObject *object = new (memoryManager) ErrorObject(this, value);
+ object->prototype = errorPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+{
+ return new (memoryManager) SyntaxErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newSyntaxErrorObject(const QString &message)
+{
+ return new (memoryManager) SyntaxErrorObject(this, message);
+}
+
+
+Object *ExecutionEngine::newReferenceErrorObject(const QString &message)
+{
+ return new (memoryManager) ReferenceErrorObject(this, message);
+}
+
+Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber)
+{
+ return new (memoryManager) ReferenceErrorObject(this, message, fileName, lineNumber);
+}
+
+
+Object *ExecutionEngine::newTypeErrorObject(const QString &message)
+{
+ return new (memoryManager) TypeErrorObject(this, message);
+}
+
+Object *ExecutionEngine::newRangeErrorObject(const QString &message)
+{
+ return new (memoryManager) RangeErrorObject(this, message);
+}
+
+Object *ExecutionEngine::newURIErrorObject(Value message)
+{
+ return new (memoryManager) URIErrorObject(this, message);
+}
+
+Object *ExecutionEngine::newVariantObject(const QVariant &v)
+{
+ return new (memoryManager) VariantObject(this, v);
+}
+
+Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o)
+{
+ return new (memoryManager) ForEachIteratorObject(ctx, o);
+}
+
+Object *ExecutionEngine::qmlContextObject() const
+{
+ ExecutionContext *ctx = current;
+
+ if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext)
+ ctx = ctx->parent;
+
+ if (!ctx->outer)
+ return 0;
+
+ while (ctx->outer && ctx->outer->type != ExecutionContext::Type_GlobalContext)
+ ctx = ctx->outer;
+
+ assert(ctx);
+ if (ctx->type != ExecutionContext::Type_QmlContext)
+ return 0;
+
+ return static_cast<CallContext *>(ctx)->activation;
+}
+
+namespace {
+ struct LineNumberResolver {
+ const ExecutionEngine* engine;
+ QScopedPointer<QV4::NativeStackTrace> nativeTrace;
+
+ LineNumberResolver(const ExecutionEngine *engine)
+ : engine(engine)
+ {
+ }
+
+ void resolve(ExecutionEngine::StackFrame *frame, ExecutionContext *context, Function *function)
+ {
+ if (context->interpreterInstructionPointer) {
+ qptrdiff offset = *context->interpreterInstructionPointer - 1 - function->codeData;
+ frame->line = function->lineNumberForProgramCounter(offset);
+ } else {
+ if (!nativeTrace)
+ nativeTrace.reset(new QV4::NativeStackTrace(engine->current));
+
+ NativeFrame nativeFrame = nativeTrace->nextFrame();
+ if (nativeFrame.function == function)
+ frame->line = nativeFrame.line;
+ }
+ }
+ };
+}
+
+QVector<ExecutionEngine::StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
+{
+ LineNumberResolver lineNumbers(this);
+
+ QVector<StackFrame> stack;
+
+ QV4::ExecutionContext *c = current;
+ while (c && frameLimit) {
+ if (CallContext *callCtx = c->asCallContext()) {
+ StackFrame frame;
+ if (callCtx->function->function)
+ frame.source = callCtx->function->function->sourceFile;
+ frame.function = callCtx->function->name->toQString();
+ frame.line = -1;
+ frame.column = -1;
+
+ if (callCtx->function->function)
+ lineNumbers.resolve(&frame, callCtx, callCtx->function->function);
+
+ stack.append(frame);
+ --frameLimit;
+ }
+ c = c->parent;
+ }
+
+ if (frameLimit && globalCode) {
+ StackFrame frame;
+ frame.source = globalCode->sourceFile;
+ frame.function = globalCode->name->toQString();
+ frame.line = -1;
+ frame.column = -1;
+
+ lineNumbers.resolve(&frame, rootContext, globalCode);
+
+ stack.append(frame);
+ }
+ return stack;
+}
+
+ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const
+{
+ StackFrame frame;
+ frame.line = -1;
+ frame.column = -1;
+
+ QVector<StackFrame> trace = stackTrace(/*limit*/ 1);
+ if (!trace.isEmpty())
+ frame = trace.first();
+
+ return frame;
+}
+
+QUrl ExecutionEngine::resolvedUrl(const QString &file)
+{
+ QUrl src(file);
+ if (!src.isRelative())
+ return src;
+
+ QUrl base;
+ QV4::ExecutionContext *c = current;
+ while (c) {
+ if (CallContext *callCtx = c->asCallContext()) {
+ if (callCtx->function->function)
+ base.setUrl(callCtx->function->function->sourceFile);
+ break;
+ }
+ c = c->parent;
+ }
+
+ if (base.isEmpty() && globalCode)
+ base.setUrl(globalCode->sourceFile);
+
+ if (base.isEmpty())
+ return src;
+
+ return base.resolved(src);
+}
+
+void ExecutionEngine::requireArgumentsAccessors(int n)
+{
+ if (n <= argumentsAccessors.size())
+ return;
+
+ uint oldSize = argumentsAccessors.size();
+ argumentsAccessors.resize(n);
+ for (int i = oldSize; i < n; ++i) {
+ FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i);
+ get->prototype = functionPrototype;
+ FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i);
+ set->prototype = functionPrototype;
+ Property pd = Property::fromAccessor(get, set);
+ argumentsAccessors[i] = pd;
+ }
+}
+
+void ExecutionEngine::markObjects()
+{
+ identifierTable->mark();
+
+ globalObject->mark();
+
+ if (globalCode)
+ globalCode->mark();
+
+ for (int i = 0; i < argumentsAccessors.size(); ++i) {
+ const Property &pd = argumentsAccessors.at(i);
+ if (FunctionObject *getter = pd.getter())
+ getter->mark();
+ if (FunctionObject *setter = pd.setter())
+ setter->mark();
+ }
+
+ ExecutionContext *c = current;
+ while (c) {
+ c->mark();
+ c = c->parent;
+ }
+
+ for (int i = 0; i < functions.size(); ++i)
+ functions.at(i)->mark();
+
+ id_length->mark();
+ id_prototype->mark();
+ id_constructor->mark();
+ id_arguments->mark();
+ id_caller->mark();
+ id_this->mark();
+ id___proto__->mark();
+ id_enumerable->mark();
+ id_configurable->mark();
+ id_writable->mark();
+ id_value->mark();
+ id_get->mark();
+ id_set->mark();
+ id_eval->mark();
+ id_uintMax->mark();
+ id_name->mark();
+
+ objectCtor.mark();
+ stringCtor.mark();
+ numberCtor.mark();
+ booleanCtor.mark();
+ arrayCtor.mark();
+ functionCtor.mark();
+ dateCtor.mark();
+ regExpCtor.mark();
+ errorCtor.mark();
+ evalErrorCtor.mark();
+ rangeErrorCtor.mark();
+ referenceErrorCtor.mark();
+ syntaxErrorCtor.mark();
+ typeErrorCtor.mark();
+ uRIErrorCtor.mark();
+
+ objectPrototype->mark();
+ stringPrototype->mark();
+ numberPrototype->mark();
+ booleanPrototype->mark();
+ arrayPrototype->mark();
+ functionPrototype->mark();
+ datePrototype->mark();
+ regExpPrototype->mark();
+ errorPrototype->mark();
+ evalErrorPrototype->mark();
+ rangeErrorPrototype->mark();
+ referenceErrorPrototype->mark();
+ syntaxErrorPrototype->mark();
+ typeErrorPrototype->mark();
+ uRIErrorPrototype->mark();
+
+ variantPrototype->mark();
+ sequencePrototype->mark();
+
+ if (m_qmlExtensions)
+ m_qmlExtensions->markObjects();
+}
+
+namespace {
+ bool functionSortHelper(Function *lhs, Function *rhs)
+ {
+ return reinterpret_cast<quintptr>(lhs->code) < reinterpret_cast<quintptr>(rhs->code);
+ }
+
+ struct FindHelper
+ {
+ bool operator()(Function *function, quintptr pc)
+ {
+ return reinterpret_cast<quintptr>(function->code) < pc
+ && (reinterpret_cast<quintptr>(function->code) + function->codeSize) < pc;
+ }
+
+ bool operator()(quintptr pc, Function *function)
+ {
+ return pc < reinterpret_cast<quintptr>(function->code);
+ }
+ };
+}
+
+Function *ExecutionEngine::functionForProgramCounter(quintptr pc) const
+{
+ if (functionsNeedSort) {
+ qSort(functions.begin(), functions.end(), functionSortHelper);
+ functionsNeedSort = false;
+ }
+
+ QVector<Function*>::ConstIterator it = qBinaryFind(functions.constBegin(), functions.constEnd(),
+ pc, FindHelper());
+ if (it != functions.constEnd())
+ return *it;
+ return 0;
+}
+
+QmlExtensions *ExecutionEngine::qmlExtensions()
+{
+ if (!m_qmlExtensions)
+ m_qmlExtensions = new QmlExtensions;
+ return m_qmlExtensions;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h
new file mode 100644
index 0000000000..20fbf03ae2
--- /dev/null
+++ b/src/qml/qml/v4/qv4engine_p.h
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ENGINE_H
+#define QV4ENGINE_H
+
+#include "qv4global_p.h"
+#include "qv4isel_p.h"
+#include "qv4util_p.h"
+#include "qv4context_p.h"
+#include "qv4property_p.h"
+#include <private/qintrusivelist_p.h>
+
+namespace WTF {
+class BumpPointerAllocator;
+}
+
+QT_BEGIN_NAMESPACE
+
+class QV8Engine;
+
+namespace QV4 {
+namespace Debugging {
+class Debugger;
+} // namespace Debugging
+}
+
+namespace QV4 {
+
+struct Function;
+struct Object;
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct ArrayObject;
+struct DateObject;
+struct FunctionObject;
+struct BoundFunction;
+struct RegExpObject;
+struct ErrorObject;
+struct ArgumentsObject;
+struct ExecutionContext;
+struct ExecutionEngine;
+class MemoryManager;
+class UnwindHelper;
+class ExecutableAllocator;
+
+struct ObjectPrototype;
+struct StringPrototype;
+struct NumberPrototype;
+struct BooleanPrototype;
+struct ArrayPrototype;
+struct FunctionPrototype;
+struct DatePrototype;
+struct RegExpPrototype;
+struct ErrorPrototype;
+struct EvalErrorPrototype;
+struct RangeErrorPrototype;
+struct ReferenceErrorPrototype;
+struct SyntaxErrorPrototype;
+struct TypeErrorPrototype;
+struct URIErrorPrototype;
+struct VariantPrototype;
+struct SequencePrototype;
+struct EvalFunction;
+struct IdentifierTable;
+struct InternalClass;
+class MultiplyWrappedQObjectMap;
+class RegExp;
+class RegExpCache;
+struct QmlExtensions;
+
+struct Q_QML_EXPORT ExecutionEngine
+{
+ MemoryManager *memoryManager;
+ ExecutableAllocator *executableAllocator;
+ ExecutableAllocator *regExpAllocator;
+ QScopedPointer<QQmlJS::EvalISelFactory> iselFactory;
+
+ ExecutionContext *current;
+ GlobalContext *rootContext;
+
+ WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
+
+ IdentifierTable *identifierTable;
+
+ QV4::Debugging::Debugger *debugger;
+
+ Object *globalObject;
+
+ Function *globalCode;
+
+ QV8Engine *v8Engine;
+
+ Value objectCtor;
+ Value stringCtor;
+ Value numberCtor;
+ Value booleanCtor;
+ Value arrayCtor;
+ Value functionCtor;
+ Value dateCtor;
+ Value regExpCtor;
+ Value errorCtor;
+ Value evalErrorCtor;
+ Value rangeErrorCtor;
+ Value referenceErrorCtor;
+ Value syntaxErrorCtor;
+ Value typeErrorCtor;
+ Value uRIErrorCtor;
+
+ ObjectPrototype *objectPrototype;
+ StringPrototype *stringPrototype;
+ NumberPrototype *numberPrototype;
+ BooleanPrototype *booleanPrototype;
+ ArrayPrototype *arrayPrototype;
+ FunctionPrototype *functionPrototype;
+ DatePrototype *datePrototype;
+ RegExpPrototype *regExpPrototype;
+ ErrorPrototype *errorPrototype;
+ EvalErrorPrototype *evalErrorPrototype;
+ RangeErrorPrototype *rangeErrorPrototype;
+ ReferenceErrorPrototype *referenceErrorPrototype;
+ SyntaxErrorPrototype *syntaxErrorPrototype;
+ TypeErrorPrototype *typeErrorPrototype;
+ URIErrorPrototype *uRIErrorPrototype;
+
+ VariantPrototype *variantPrototype;
+ SequencePrototype *sequencePrototype;
+
+ QQmlJS::MemoryPool classPool;
+ InternalClass *emptyClass;
+ InternalClass *arrayClass;
+
+ EvalFunction *evalFunction;
+
+ QVector<Property> argumentsAccessors;
+
+ String *id_undefined;
+ String *id_null;
+ String *id_true;
+ String *id_false;
+ String *id_boolean;
+ String *id_number;
+ String *id_string;
+ String *id_object;
+ String *id_function;
+ String *id_length;
+ String *id_prototype;
+ String *id_constructor;
+ String *id_arguments;
+ String *id_caller;
+ String *id_this;
+ String *id___proto__;
+ String *id_enumerable;
+ String *id_configurable;
+ String *id_writable;
+ String *id_value;
+ String *id_get;
+ String *id_set;
+ String *id_eval;
+ String *id_uintMax;
+ String *id_name;
+
+ mutable QVector<Function *> functions;
+ mutable bool functionsNeedSort;
+
+ quint32 m_engineId;
+
+ RegExpCache *regExpCache;
+
+ // Scarce resources are "exceptionally high cost" QVariant types where allowing the
+ // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other
+ // out-of-resource situations. When such a resource is passed into JavaScript we
+ // add it to the scarceResources list and it is destroyed when we return from the
+ // JavaScript execution that created it. The user can prevent this behavior by
+ // calling preserve() on the object which removes it from this scarceResource list.
+ class ScarceResourceData {
+ public:
+ ScarceResourceData(const QVariant &data) : data(data) {}
+ QVariant data;
+ QIntrusiveListNode node;
+ };
+ QIntrusiveList<ScarceResourceData, &ScarceResourceData::node> scarceResources;
+
+ // Normally the JS wrappers for QObjects are stored in the QQmlData/QObjectPrivate,
+ // but any time a QObject is wrapped a second time in another engine, we have to do
+ // bookkeeping.
+ MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects;
+
+ ExecutionEngine(QQmlJS::EvalISelFactory *iselFactory = 0);
+ ~ExecutionEngine();
+
+ void enableDebugger();
+
+ WithContext *newWithContext(Object *with);
+ CatchContext *newCatchContext(String* exceptionVarName, const QV4::Value &exceptionValue);
+ CallContext *newCallContext(FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc);
+ CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc);
+ CallContext *newQmlContext(FunctionObject *f, Object *qml);
+ ExecutionContext *pushGlobalContext();
+ void pushContext(SimpleCallContext *context);
+ ExecutionContext *popContext();
+
+ Function *newFunction(const QString &name);
+
+ FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *));
+ FunctionObject *newScriptFunction(ExecutionContext *scope, Function *function);
+ BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
+
+ Object *newObject();
+ Object *newObject(InternalClass *internalClass);
+
+ String *newString(const QString &s);
+ String *newIdentifier(const QString &text);
+
+ Object *newStringObject(const Value &value);
+ Object *newNumberObject(const Value &value);
+ Object *newBooleanObject(const Value &value);
+
+ ArrayObject *newArrayObject(int count = 0);
+ ArrayObject *newArrayObject(const QStringList &list);
+
+ DateObject *newDateObject(const Value &value);
+ DateObject *newDateObject(const QDateTime &dt);
+
+ RegExpObject *newRegExpObject(const QString &pattern, int flags);
+ RegExpObject *newRegExpObject(RegExp* re, bool global);
+ RegExpObject *newRegExpObject(const QRegExp &re);
+
+ Object *newErrorObject(const Value &value);
+ Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message);
+ Object *newSyntaxErrorObject(const QString &message);
+ Object *newReferenceErrorObject(const QString &message);
+ Object *newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber);
+ Object *newTypeErrorObject(const QString &message);
+ Object *newRangeErrorObject(const QString &message);
+ Object *newURIErrorObject(Value message);
+
+ Object *newVariantObject(const QVariant &v);
+
+ Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o);
+
+ Object *qmlContextObject() const;
+
+ struct StackFrame {
+ QString source;
+ QString function;
+ int line;
+ int column;
+ };
+ typedef QVector<StackFrame> StackTrace;
+ StackTrace stackTrace(int frameLimit = -1) const;
+ StackFrame currentStackFrame() const;
+ QUrl resolvedUrl(const QString &file);
+
+ void requireArgumentsAccessors(int n);
+
+ void markObjects();
+
+ void initRootContext();
+
+ InternalClass *newClass(const InternalClass &other);
+
+ Function *functionForProgramCounter(quintptr pc) const;
+
+ QmlExtensions *qmlExtensions();
+
+private:
+ QmlExtensions *m_qmlExtensions;
+};
+
+inline void ExecutionEngine::pushContext(SimpleCallContext *context)
+{
+ context->parent = current;
+ current = context;
+ current->currentEvalCode = 0;
+}
+
+inline ExecutionContext *ExecutionEngine::popContext()
+{
+ CallContext *c = current->asCallContext();
+ if (c && !c->needsOwnArguments()) {
+ c->arguments = 0;
+ c->argumentCount = 0;
+ }
+
+ current = current->parent;
+ return current;
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4ENGINE_H
diff --git a/src/qml/qml/v4/qv4errorobject.cpp b/src/qml/qml/v4/qv4errorobject.cpp
new file mode 100644
index 0000000000..516a4d37f8
--- /dev/null
+++ b/src/qml/qml/v4/qv4errorobject.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4errorobject_p.h"
+#include "qv4mm_p.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDateTime>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+#include <cmath>
+#include <qmath.h>
+#include <qnumeric.h>
+#include <cassert>
+
+#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 <qv4isel_masm_p.h>
+
+#ifndef Q_OS_WIN
+# include <time.h>
+# ifndef Q_OS_VXWORKS
+# include <sys/time.h>
+# else
+# include "qplatformdefs.h"
+# endif
+#else
+# include <windows.h>
+#endif
+
+using namespace QV4;
+
+ErrorObject::ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t)
+ : Object(engine)
+ , stack(0)
+{
+ type = Type_ErrorObject;
+ vtbl = &static_vtbl;
+ subtype = t;
+ defineAccessorProperty(engine, QStringLiteral("stack"), ErrorObject::method_get_stack, 0);
+
+ if (!message.isUndefined())
+ defineDefaultProperty(engine->newString(QStringLiteral("message")), message);
+ defineDefaultProperty(engine, QStringLiteral("name"), Value::fromString(engine, className()));
+
+ stackTrace = engine->stackTrace();
+ if (!stackTrace.isEmpty()) {
+ defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine, stackTrace.at(0).source));
+ defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(stackTrace.at(0).line));
+ }
+}
+
+Value ErrorObject::method_get_stack(SimpleCallContext *ctx)
+{
+ ErrorObject *This = ctx->thisObject.asErrorObject();
+ if (!This)
+ ctx->throwTypeError();
+ if (!This->stack) {
+ QString trace;
+ for (int i = 0; i < This->stackTrace.count(); ++i) {
+ if (i > 0)
+ trace += QLatin1Char('\n');
+ const ExecutionEngine::StackFrame &frame = This->stackTrace[i];
+ trace += frame.function;
+ trace += QLatin1Char('@');
+ trace += frame.source;
+ if (frame.line >= 0) {
+ trace += QLatin1Char(':');
+ trace += QString::number(frame.line);
+ }
+ }
+ This->stack = ctx->engine->newString(trace);
+ }
+ return Value::fromString(This->stack);
+}
+
+void ErrorObject::markObjects(Managed *that)
+{
+ ErrorObject *This = that->asErrorObject();
+ if (This->stack)
+ This->stack->mark();
+ Object::markObjects(that);
+}
+
+DEFINE_MANAGED_VTABLE(ErrorObject);
+
+DEFINE_MANAGED_VTABLE(SyntaxErrorObject);
+
+SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg)
+ : ErrorObject(engine, msg, SyntaxError)
+ , msg(0)
+{
+ vtbl = &static_vtbl;
+ prototype = engine->syntaxErrorPrototype;
+}
+
+SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg)
+ : ErrorObject(engine, Value::fromString(engine, msg), SyntaxError)
+ , msg(0)
+{
+ vtbl = &static_vtbl;
+ prototype = engine->syntaxErrorPrototype;
+}
+
+SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+ : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError)
+ , msg(message)
+{
+ vtbl = &static_vtbl;
+ prototype = ctx->engine->syntaxErrorPrototype;
+ if (message) {
+ defineDefaultProperty(ctx->engine, QStringLiteral("fileName"), Value::fromString(ctx, message->fileName));
+ defineDefaultProperty(ctx->engine, QStringLiteral("lineNumber"), Value::fromInt32(message->startLine));
+ }
+}
+
+
+
+EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message)
+ : ErrorObject(engine, message, EvalError)
+{
+ prototype = engine->evalErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message)
+ : ErrorObject(engine, message, RangeError)
+{
+ prototype = engine->rangeErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message)
+ : ErrorObject(engine, Value::fromString(engine, message), RangeError)
+{
+ prototype = engine->rangeErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message)
+ : ErrorObject(engine, message, ReferenceError)
+{
+ prototype = engine->referenceErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message)
+ : ErrorObject(engine, Value::fromString(engine, message), ReferenceError)
+{
+ prototype = engine->referenceErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber)
+ : ErrorObject(engine, Value::fromString(engine, msg), ReferenceError)
+{
+ prototype = engine->referenceErrorPrototype;
+ defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine->rootContext, fileName));
+ defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(lineNumber));
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message)
+ : ErrorObject(engine, message, TypeError)
+{
+ prototype = engine->typeErrorPrototype;
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message)
+ : ErrorObject(engine, Value::fromString(engine, message), TypeError)
+{
+ prototype = engine->typeErrorPrototype;
+}
+
+URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message)
+ : ErrorObject(engine, message, URIError)
+{
+ prototype = engine->uRIErrorPrototype;
+}
+
+DEFINE_MANAGED_VTABLE(ErrorCtor);
+DEFINE_MANAGED_VTABLE(EvalErrorCtor);
+DEFINE_MANAGED_VTABLE(RangeErrorCtor);
+DEFINE_MANAGED_VTABLE(ReferenceErrorCtor);
+DEFINE_MANAGED_VTABLE(SyntaxErrorCtor);
+DEFINE_MANAGED_VTABLE(TypeErrorCtor);
+DEFINE_MANAGED_VTABLE(URIErrorCtor);
+
+ErrorCtor::ErrorCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Error")))
+{
+ vtbl = &static_vtbl;
+}
+
+ErrorCtor::ErrorCtor(ExecutionContext *scope, String *name)
+ : FunctionObject(scope, name)
+{
+ vtbl = &static_vtbl;
+}
+
+Value ErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(m->engine()->newErrorObject(argc ? args[0] : Value::undefinedValue()));
+}
+
+Value ErrorCtor::call(Managed *that, const Value &, Value *args, int argc)
+{
+ return that->construct(args, argc);
+}
+
+EvalErrorCtor::EvalErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("EvalError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value EvalErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) EvalErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+RangeErrorCtor::RangeErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("RangeError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value RangeErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) RangeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+ReferenceErrorCtor::ReferenceErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("ReferenceError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value ReferenceErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) ReferenceErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+SyntaxErrorCtor::SyntaxErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("SyntaxError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value SyntaxErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) SyntaxErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+TypeErrorCtor::TypeErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("TypeError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value TypeErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) TypeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+URIErrorCtor::URIErrorCtor(ExecutionContext *scope)
+ : ErrorCtor(scope, scope->engine->newIdentifier("URIError"))
+{
+ vtbl = &static_vtbl;
+}
+
+Value URIErrorCtor::construct(Managed *m, Value *args, int argc)
+{
+ return Value::fromObject(new (m->engine()->memoryManager) URIErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue()));
+}
+
+void ErrorPrototype::init(ExecutionEngine *engine, const Value &ctor, Object *obj)
+{
+ ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(obj));
+ ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1));
+ obj->defineDefaultProperty(engine, QStringLiteral("constructor"), ctor);
+ obj->defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0);
+ obj->defineDefaultProperty(engine, QStringLiteral("message"), Value::fromString(engine, QString()));
+}
+
+Value ErrorPrototype::method_toString(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ Value name = o->get(ctx->engine->newString(QString::fromLatin1("name")));
+ QString qname;
+ if (name.isUndefined())
+ qname = QString::fromLatin1("Error");
+ else
+ qname = __qmljs_to_string(name, ctx).stringValue()->toQString();
+
+ Value message = o->get(ctx->engine->newString(QString::fromLatin1("message")));
+ QString qmessage;
+ if (!message.isUndefined())
+ qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString();
+
+ QString str;
+ if (qname.isEmpty()) {
+ str = qmessage;
+ } else if (qmessage.isEmpty()) {
+ str = qname;
+ } else {
+ str = qname + QLatin1String(": ") + qmessage;
+ }
+
+ return Value::fromString(ctx, str);
+}
diff --git a/src/qml/qml/v4/qv4errorobject_p.h b/src/qml/qml/v4/qv4errorobject_p.h
new file mode 100644
index 0000000000..d3e0f107bc
--- /dev/null
+++ b/src/qml/qml/v4/qv4errorobject_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ERROROBJECT_H
+#define QV4ERROROBJECT_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct SyntaxErrorObject;
+
+struct ErrorObject: Object {
+ Q_MANAGED
+
+ enum ErrorType {
+ Error,
+ EvalError,
+ RangeError,
+ ReferenceError,
+ SyntaxError,
+ TypeError,
+ URIError
+ };
+
+ ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t = Error);
+
+ SyntaxErrorObject *asSyntaxError();
+
+ ExecutionEngine::StackTrace stackTrace;
+ String *stack;
+
+ static Value method_get_stack(SimpleCallContext *ctx);
+ static void markObjects(Managed *that);
+ static void destroy(Managed *that) { static_cast<ErrorObject *>(that)->~ErrorObject(); }
+};
+
+struct EvalErrorObject: ErrorObject {
+ EvalErrorObject(ExecutionEngine *engine, const Value &message);
+};
+
+struct RangeErrorObject: ErrorObject {
+ RangeErrorObject(ExecutionEngine *engine, const Value &message);
+ RangeErrorObject(ExecutionEngine *engine, const QString &msg);
+};
+
+struct ReferenceErrorObject: ErrorObject {
+ ReferenceErrorObject(ExecutionEngine *engine, const Value &message);
+ ReferenceErrorObject(ExecutionEngine *engine, const QString &msg);
+ ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber);
+};
+
+struct SyntaxErrorObject: ErrorObject {
+ SyntaxErrorObject(ExecutionEngine *engine, const Value &msg);
+ SyntaxErrorObject(ExecutionEngine *engine, const QString &msg);
+ SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg);
+ ~SyntaxErrorObject() { delete msg; }
+ static void destroy(Managed *that) { static_cast<SyntaxErrorObject *>(that)->~SyntaxErrorObject(); }
+
+ DiagnosticMessage *message() { return msg; }
+
+private:
+ DiagnosticMessage *msg;
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct TypeErrorObject: ErrorObject {
+ TypeErrorObject(ExecutionEngine *engine, const Value &message);
+ TypeErrorObject(ExecutionEngine *engine, const QString &msg);
+};
+
+struct URIErrorObject: ErrorObject {
+ URIErrorObject(ExecutionEngine *engine, const Value &message);
+};
+
+struct ErrorCtor: FunctionObject
+{
+ ErrorCtor(ExecutionContext *scope);
+ ErrorCtor(ExecutionContext *scope, String *name);
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct EvalErrorCtor: ErrorCtor
+{
+ EvalErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct RangeErrorCtor: ErrorCtor
+{
+ RangeErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ReferenceErrorCtor: ErrorCtor
+{
+ ReferenceErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct SyntaxErrorCtor: ErrorCtor
+{
+ SyntaxErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct TypeErrorCtor: ErrorCtor
+{
+ TypeErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct URIErrorCtor: ErrorCtor
+{
+ URIErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+
+struct ErrorPrototype: ErrorObject
+{
+ // ### shouldn't be undefined
+ ErrorPrototype(ExecutionEngine *engine): ErrorObject(engine, Value::undefinedValue()) {}
+ void init(ExecutionEngine *engine, const Value &ctor) { init(engine, ctor, this); }
+
+ static void init(ExecutionEngine *engine, const Value &ctor, Object *obj);
+ static Value method_toString(SimpleCallContext *ctx);
+};
+
+struct EvalErrorPrototype: EvalErrorObject
+{
+ EvalErrorPrototype(ExecutionEngine *engine): EvalErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+struct RangeErrorPrototype: RangeErrorObject
+{
+ RangeErrorPrototype(ExecutionEngine *engine): RangeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+struct ReferenceErrorPrototype: ReferenceErrorObject
+{
+ ReferenceErrorPrototype(ExecutionEngine *engine): ReferenceErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+struct SyntaxErrorPrototype: SyntaxErrorObject
+{
+ SyntaxErrorPrototype(ExecutionEngine *engine): SyntaxErrorObject(engine, 0) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+struct TypeErrorPrototype: TypeErrorObject
+{
+ TypeErrorPrototype(ExecutionEngine *engine): TypeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+struct URIErrorPrototype: URIErrorObject
+{
+ URIErrorPrototype(ExecutionEngine *engine): URIErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); }
+};
+
+
+inline SyntaxErrorObject *ErrorObject::asSyntaxError()
+{
+ return subtype == SyntaxError ? static_cast<SyntaxErrorObject *>(this) : 0;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4exception.cpp b/src/qml/qml/v4/qv4exception.cpp
new file mode 100644
index 0000000000..9f15c27ffc
--- /dev/null
+++ b/src/qml/qml/v4/qv4exception.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4exception_p.h"
+#include "qv4errorobject_p.h"
+#include "qv4debugging_p.h"
+#include "qv4unwindhelper_p.h"
+
+#include <wtf/Platform.h>
+
+#if USE(LIBUNWIND_DEBUG)
+#include <libunwind.h>
+#include <execinfo.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+
+void Exception::throwException(ExecutionContext *context, const Value &value)
+{
+ if (context->engine->debugger)
+ context->engine->debugger->aboutToThrow(value);
+
+ UnwindHelper::prepareForUnwind(context);
+
+#if USE(LIBUNWIND_DEBUG)
+ printf("about to throw exception. walking stack first with libunwind:\n");
+ unw_cursor_t cursor; unw_context_t uc;
+ unw_word_t ip, sp;
+
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+ while (unw_step(&cursor) > 0) {
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ unw_get_reg(&cursor, UNW_REG_SP, &sp);
+ printf("ip = %lx, sp = %lx ", (long) ip, (long) sp);
+ void * const addr = (void*)ip;
+ char **symbol = backtrace_symbols(&addr, 1);
+ printf("%s", symbol[0]);
+ free(symbol);
+ printf("\n");
+ }
+ printf("stack walked. throwing exception now...\n");
+#endif
+
+ throwInternal(context, value);
+}
+
+Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue)
+ : exception(exceptionValue)
+{
+ this->throwingContext = throwingContext->engine->current;
+ accepted = false;
+ if (ErrorObject *error = exceptionValue.asErrorObject())
+ m_stackTrace = error->stackTrace;
+ else
+ m_stackTrace = throwingContext->engine->stackTrace();
+}
+
+Exception::~Exception()
+{
+ assert(accepted);
+}
+
+void Exception::accept(ExecutionContext *catchingContext)
+{
+ assert(!accepted);
+ accepted = true;
+ partiallyUnwindContext(catchingContext);
+}
+
+void Exception::partiallyUnwindContext(ExecutionContext *catchingContext)
+{
+ if (!throwingContext)
+ return;
+ ExecutionContext *context = throwingContext;
+ while (context != catchingContext)
+ context = context->engine->popContext();
+ throwingContext = context;
+}
+
+#if !defined(V4_CXX_ABI_EXCEPTION)
+void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue)
+{
+ throw Exception(throwingContext, exceptionValue);
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4exception_gcc.cpp b/src/qml/qml/v4/qv4exception_gcc.cpp
new file mode 100644
index 0000000000..0324a06e0b
--- /dev/null
+++ b/src/qml/qml/v4/qv4exception_gcc.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4exception_p.h"
+
+#include <unwind.h>
+#include <cxxabi.h>
+#include <bits/atomic_word.h>
+#include <typeinfo>
+#include <exception>
+
+/*
+ * This is a little bit hacky as it relies on the fact that exceptions are
+ * reference counted in libstdc++ and that affects the layout of the standardized
+ * cxa_exception, making it bigger. LLVM's libcxxabi stores the reference count
+ * differently, so this here is entirely GNU libstdc++ specific.
+ *
+ * Eliminating this dependency is doable but requires replacing the use of C++ exceptions
+ * with foreign exceptions (a different exception class) and then using __cxa_get_globals
+ * to get hold of the exception inside the catch (...). AFAICS that would be portable.
+ */
+
+namespace {
+
+// 2.1.1 from http://mentorembedded.github.io/cxx-abi/abi-eh.html
+struct cxa_exception {
+ std::type_info *typeInfo;
+ void (*exceptionDestructor)(void*);
+ std::unexpected_handler unexpectedHandler;
+ std::terminate_handler terminateHandler;
+ cxa_exception *nextException;
+ int handlerCount;
+#ifdef __ARM_EABI_UNWINDER__
+ cxa_exception *nextPropagatingException;
+ int propagationCount;
+#else
+ int handlerSwitchValue;
+ const char *actionRecord;
+ const char *languageSpecificData;
+ void *catchTemp;
+ void *adjustedPtr;
+#endif
+ _Unwind_Exception unwindHeader;
+};
+
+// This is what libstdc++ actually allocates
+struct gcc_refcounted_compatible_exception {
+ _Atomic_word refCount;
+ cxa_exception x;
+};
+
+}
+
+static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex)
+{
+ gcc_refcounted_compatible_exception *exception = reinterpret_cast<gcc_refcounted_compatible_exception *>(ex + 1) - 1;
+ if (!--exception->refCount) {
+ if (exception->x.exceptionDestructor)
+ exception->x.exceptionDestructor(ex + 1);
+ abi::__cxa_free_exception(ex + 1);
+ }
+}
+
+static void exception_destructor(void *ex)
+{
+ reinterpret_cast<QV4::Exception *>(ex)->~Exception();
+}
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue)
+{
+ void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception));
+ gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast<gcc_refcounted_compatible_exception *>(rawException) - 1;
+ cxa_exception *exception = &refCountedException->x;
+
+ (void)new (rawException) Exception(throwingContext, exceptionValue);
+
+ refCountedException->refCount = 1;
+ exception->typeInfo = const_cast<std::type_info*>(&typeid(Exception));
+ exception->exceptionDestructor = &exception_destructor;
+ exception->unexpectedHandler = std::unexpected;
+ exception->terminateHandler = std::terminate;
+ exception->unwindHeader.exception_cleanup = &exception_cleanup;
+#ifdef __ARM_EABI_UNWINDER__
+ exception->unwindHeader.exception_class[0] = 'G';
+ exception->unwindHeader.exception_class[1] = 'N';
+ exception->unwindHeader.exception_class[2] = 'U';
+ exception->unwindHeader.exception_class[3] = 'C';
+ exception->unwindHeader.exception_class[4] = 'C';
+ exception->unwindHeader.exception_class[5] = '+';
+ exception->unwindHeader.exception_class[6] = '+';
+ exception->unwindHeader.exception_class[7] = 0;
+#else
+ exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0
+#endif
+
+ _Unwind_RaiseException(&exception->unwindHeader);
+ abi::__cxa_begin_catch(rawException);
+ std::terminate();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4exception_p.h b/src/qml/qml/v4/qv4exception_p.h
new file mode 100644
index 0000000000..8ba06f57f4
--- /dev/null
+++ b/src/qml/qml/v4/qv4exception_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4EXCEPTION_GNU_P
+#define QV4EXCEPTION_GNU_P
+
+#include <qglobal.h>
+#include "qv4value_p.h"
+#include "qv4engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Q_QML_EXPORT Exception {
+ static void throwException(ExecutionContext *throwingContext, const Value &exceptionValue);
+
+ ~Exception();
+
+ void accept(ExecutionContext *catchingContext);
+
+ void partiallyUnwindContext(ExecutionContext *catchingContext);
+
+ Value value() const { return exception; }
+
+ ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; }
+
+private:
+ void *operator new(size_t, void *p) { return p; }
+
+ explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue);
+
+ ExecutionContext *throwingContext;
+ bool accepted;
+ PersistentValue exception;
+ ExecutionEngine::StackTrace m_stackTrace;
+ static void throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue);
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4EXCEPTION_GNU_P
diff --git a/src/qml/qml/v4/qv4executableallocator.cpp b/src/qml/qml/v4/qv4executableallocator.cpp
new file mode 100644
index 0000000000..a754663556
--- /dev/null
+++ b/src/qml/qml/v4/qv4executableallocator.cpp
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4executableallocator_p.h"
+
+#include <assert.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/PageAllocation.h>
+
+using namespace QV4;
+
+void *ExecutableAllocator::Allocation::start() const
+{
+ return reinterpret_cast<void*>(addr);
+}
+
+ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize)
+{
+ Allocation *remainder = new Allocation;
+ if (next)
+ next->prev = remainder;
+
+ remainder->next = next;
+ next = remainder;
+
+ remainder->prev = this;
+
+ remainder->size = size - dividingSize;
+ remainder->free = free;
+ remainder->addr = addr + dividingSize;
+ size = dividingSize;
+
+ return remainder;
+}
+
+bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator)
+{
+ assert(free);
+ if (!next || !next->free)
+ return false;
+
+ allocator->freeAllocations.remove(size, this);
+ allocator->freeAllocations.remove(next->size, next);
+
+ size += next->size;
+ Allocation *newNext = next->next;
+ delete next;
+ next = newNext;
+ if (next)
+ next->prev = this;
+
+ allocator->freeAllocations.insert(size, this);
+ return true;
+}
+
+bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator)
+{
+ assert(free);
+ if (!prev || !prev->free)
+ return false;
+
+ allocator->freeAllocations.remove(size, this);
+ allocator->freeAllocations.remove(prev->size, prev);
+
+ prev->size += size;
+ if (next)
+ next->prev = prev;
+ prev->next = next;
+
+ allocator->freeAllocations.insert(prev->size, prev);
+
+ delete this;
+ return true;
+}
+
+ExecutableAllocator::ChunkOfPages::~ChunkOfPages()
+{
+ delete unwindInfo;
+ Allocation *alloc = firstAllocation;
+ while (alloc) {
+ Allocation *next = alloc->next;
+ delete alloc;
+ alloc = next;
+ }
+ pages->deallocate();
+ delete pages;
+}
+
+bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const
+{
+ Allocation *it = firstAllocation;
+ while (it) {
+ if (it == alloc)
+ return true;
+ it = it->next;
+ }
+ return false;
+}
+
+ExecutableAllocator::~ExecutableAllocator()
+{
+ qDeleteAll(chunks);
+}
+
+ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size)
+{
+ Allocation *allocation = 0;
+
+ // Code is best aligned to 16-byte boundaries.
+ size = WTF::roundUpToMultipleOf(16, size);
+
+ QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size);
+ if (it != freeAllocations.end()) {
+ allocation = *it;
+ freeAllocations.erase(it);
+ }
+
+ if (!allocation) {
+ ChunkOfPages *chunk = new ChunkOfPages;
+ size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size);
+ chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages));
+ chunks.insert(reinterpret_cast<quintptr>(chunk->pages->base()) - 1, chunk);
+ allocation = new Allocation;
+ allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base());
+ allocation->size = allocSize;
+ allocation->free = true;
+ chunk->firstAllocation = allocation;
+ }
+
+ assert(allocation);
+ assert(allocation->free);
+
+ allocation->free = false;
+
+ if (allocation->size > size) {
+ Allocation *remainder = allocation->split(size);
+ remainder->free = true;
+ if (!remainder->mergeNext(this))
+ freeAllocations.insert(remainder->size, remainder);
+ }
+
+ return allocation;
+}
+
+void ExecutableAllocator::free(Allocation *allocation)
+{
+ assert(allocation);
+
+ allocation->free = true;
+
+ QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(allocation->addr);
+ if (it != chunks.begin())
+ --it;
+ assert(it != chunks.end());
+ ChunkOfPages *chunk = *it;
+ assert(chunk->contains(allocation));
+
+ bool merged = allocation->mergeNext(this);
+ merged |= allocation->mergePrevious(this);
+ if (!merged)
+ freeAllocations.insert(allocation->size, allocation);
+
+ allocation = 0;
+
+ if (!chunk->firstAllocation->next) {
+ freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation);
+ chunks.erase(it);
+ delete chunk;
+ return;
+ }
+}
+
+ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const
+{
+ QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(allocation->addr);
+ if (it != chunks.begin())
+ --it;
+ if (it == chunks.end())
+ return 0;
+ return *it;
+}
+
diff --git a/src/qml/qml/v4/qv4executableallocator_p.h b/src/qml/qml/v4/qv4executableallocator_p.h
new file mode 100644
index 0000000000..2a304baf9c
--- /dev/null
+++ b/src/qml/qml/v4/qv4executableallocator_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4EXECUTABLEALLOCATOR_H
+#define QV4EXECUTABLEALLOCATOR_H
+
+#include "qv4global_p.h"
+
+#include <QMultiMap>
+#include <QHash>
+#include <QVector>
+#include <QByteArray>
+
+namespace WTF {
+class PageAllocation;
+};
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class Q_AUTOTEST_EXPORT ExecutableAllocator
+{
+public:
+ struct ChunkOfPages;
+ struct Allocation;
+
+ ~ExecutableAllocator();
+
+ Allocation *allocate(size_t size);
+ void free(Allocation *allocation);
+
+ struct Allocation
+ {
+ Allocation()
+ : addr(0)
+ , size(0)
+ , free(true)
+ , next(0)
+ , prev(0)
+ {}
+
+ void *start() const;
+
+ private:
+ friend class ExecutableAllocator;
+
+ Allocation *split(size_t dividingSize);
+ bool mergeNext(ExecutableAllocator *allocator);
+ bool mergePrevious(ExecutableAllocator *allocator);
+
+ quintptr addr;
+ uint size : 31; // More than 2GB of function code? nah :)
+ uint free : 1;
+ Allocation *next;
+ Allocation *prev;
+ };
+
+ // for debugging / unit-testing
+ int freeAllocationCount() const { return freeAllocations.count(); }
+ int chunkCount() const { return chunks.count(); }
+
+ // Used to store CIE+FDE on x86/x86-64.
+ struct PlatformUnwindInfo
+ {
+ virtual ~PlatformUnwindInfo() {}
+ };
+
+ struct ChunkOfPages
+ {
+ ChunkOfPages()
+ : pages(0)
+ , firstAllocation(0)
+ , unwindInfo(0)
+ {}
+ ~ChunkOfPages();
+
+ WTF::PageAllocation *pages;
+ Allocation *firstAllocation;
+ PlatformUnwindInfo *unwindInfo;
+
+ bool contains(Allocation *alloc) const;
+ };
+
+ ChunkOfPages *chunkForAllocation(Allocation *allocation) const;
+
+private:
+ QMultiMap<size_t, Allocation*> freeAllocations;
+ QMap<quintptr, ChunkOfPages*> chunks;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4EXECUTABLEALLOCATOR_H
diff --git a/src/qml/qml/v4/qv4function.cpp b/src/qml/qml/v4/qv4function.cpp
new file mode 100644
index 0000000000..bf633a9b41
--- /dev/null
+++ b/src/qml/qml/v4/qv4function.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4function_p.h"
+#include "qv4managed_p.h"
+#include "qv4string_p.h"
+#include "qv4value_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+Function::~Function()
+{
+ delete[] codeData;
+}
+
+void Function::mark()
+{
+ if (name)
+ name->mark();
+ for (int i = 0; i < formals.size(); ++i)
+ formals.at(i)->mark();
+ for (int i = 0; i < locals.size(); ++i)
+ locals.at(i)->mark();
+ for (int i = 0; i < generatedValues.size(); ++i)
+ if (Managed *m = generatedValues.at(i).asManaged())
+ m->mark();
+ for (int i = 0; i < identifiers.size(); ++i)
+ identifiers.at(i)->mark();
+}
+
+namespace QV4 {
+bool operator<(const LineNumberMapping &mapping, qptrdiff pc)
+{
+ return mapping.codeOffset < pc;
+}
+}
+
+int Function::lineNumberForProgramCounter(qptrdiff offset) const
+{
+ QVector<LineNumberMapping>::ConstIterator it = qLowerBound(lineNumberMappings.begin(), lineNumberMappings.end(), offset);
+ if (it != lineNumberMappings.constBegin() && lineNumberMappings.count() > 0)
+ --it;
+ if (it == lineNumberMappings.constEnd())
+ return -1;
+ return it->lineNumber;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4function_p.h b/src/qml/qml/v4/qv4function_p.h
new file mode 100644
index 0000000000..4dd0533d51
--- /dev/null
+++ b/src/qml/qml/v4/qv4function_p.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4FUNCTION_H
+#define QV4FUNCTION_H
+
+#include "qv4global_p.h"
+
+#include <QtCore/QVector>
+#include <QtCore/QByteArray>
+#include <QtCore/qurl.h>
+
+#include <config.h>
+#include <assembler/MacroAssemblerCodeRef.h>
+#include "qv4value_def_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct String;
+struct Function;
+struct Object;
+struct FunctionObject;
+struct ExecutionContext;
+struct ExecutionEngine;
+class MemoryManager;
+
+struct ObjectPrototype;
+struct StringPrototype;
+struct NumberPrototype;
+struct BooleanPrototype;
+struct ArrayPrototype;
+struct FunctionPrototype;
+struct DatePrototype;
+struct ErrorPrototype;
+struct EvalErrorPrototype;
+struct RangeErrorPrototype;
+struct ReferenceErrorPrototype;
+struct SyntaxErrorPrototype;
+struct TypeErrorPrototype;
+struct URIErrorPrototype;
+struct InternalClass;
+struct Lookup;
+
+struct LineNumberMapping
+{
+ quint32 codeOffset;
+ int lineNumber;
+};
+
+struct Function {
+ String *name;
+
+ Value (*code)(ExecutionContext *, const uchar *);
+ const uchar *codeData;
+ JSC::MacroAssemblerCodeRef codeRef;
+ quint32 codeSize;
+
+ QVector<String *> formals;
+ QVector<String *> locals;
+ QVector<Value> generatedValues;
+ QVector<String *> identifiers;
+ QVector<Function *> nestedFunctions;
+ Function *outer;
+
+ Lookup *lookups;
+
+ bool hasNestedFunctions;
+ bool hasDirectEval;
+ bool usesArgumentsObject;
+ bool isStrict;
+ bool isNamedExpression;
+
+ QString sourceFile;
+ QVector<LineNumberMapping> lineNumberMappings;
+
+ Function(String *name)
+ : name(name)
+ , code(0)
+ , codeData(0)
+ , codeSize(0)
+ , outer(0)
+ , lookups(0)
+ , hasNestedFunctions(0)
+ , hasDirectEval(false)
+ , usesArgumentsObject(false)
+ , isStrict(false)
+ , isNamedExpression(false)
+ {}
+ ~Function();
+
+ inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; }
+
+ void mark();
+
+ int lineNumberForProgramCounter(qptrdiff offset) const;
+};
+
+}
+Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4functionobject.cpp b/src/qml/qml/v4/qv4functionobject.cpp
new file mode 100644
index 0000000000..ffdd08ed0a
--- /dev/null
+++ b/src/qml/qml/v4/qv4functionobject.cpp
@@ -0,0 +1,558 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4object_p.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4stringobject_p.h"
+#include "qv4function_p.h"
+#include "qv4mm_p.h"
+#include "qv4exception_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/qmath.h>
+#include <QtCore/QDebug>
+#include <cassert>
+#include <typeinfo>
+#include <iostream>
+#include "qv4alloca_p.h"
+
+using namespace QV4;
+
+
+DEFINE_MANAGED_VTABLE(FunctionObject);
+
+FunctionObject::FunctionObject(ExecutionContext *scope, String *name)
+ : Object(scope->engine)
+ , scope(scope)
+ , name(name)
+ , formalParameterList(0)
+ , varList(0)
+ , formalParameterCount(0)
+ , varCount(0)
+ , function(0)
+{
+ vtbl = &static_vtbl;
+ prototype = scope->engine->functionPrototype;
+
+ type = Type_FunctionObject;
+ needsActivation = true;
+ usesArgumentsObject = false;
+ strictMode = false;
+#ifndef QT_NO_DEBUG
+ assert(scope->next != (ExecutionContext *)0x1);
+#endif
+
+ if (name)
+ defineReadonlyProperty(scope->engine->id_name, Value::fromString(name));
+}
+
+Value FunctionObject::newInstance()
+{
+ return construct(0, 0);
+}
+
+bool FunctionObject::hasInstance(Managed *that, const Value &value)
+{
+ FunctionObject *f = static_cast<FunctionObject *>(that);
+
+ Object *v = value.asObject();
+ if (!v)
+ return false;
+
+ ExecutionContext *ctx = f->engine()->current;
+ Object *o = f->get(ctx->engine->id_prototype).asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ while (v) {
+ v = v->prototype;
+
+ if (! v)
+ break;
+ else if (o == v)
+ return true;
+ }
+
+ return false;
+}
+
+Value FunctionObject::construct(Managed *that, Value *, int)
+{
+ FunctionObject *f = static_cast<FunctionObject *>(that);
+ ExecutionEngine *v4 = f->engine();
+
+ Object *obj = v4->newObject();
+ Value proto = f->get(v4->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+ return Value::fromObject(obj);
+}
+
+Value FunctionObject::call(Managed *, const Value &, Value *, int)
+{
+ return Value::undefinedValue();
+}
+
+void FunctionObject::markObjects(Managed *that)
+{
+ FunctionObject *o = static_cast<FunctionObject *>(that);
+ if (o->name)
+ o->name->mark();
+ // these are marked in VM::Function:
+// for (uint i = 0; i < formalParameterCount; ++i)
+// formalParameterList[i]->mark();
+// for (uint i = 0; i < varCount; ++i)
+// varList[i]->mark();
+ o->scope->mark();
+ if (o->function)
+ o->function->mark();
+
+ Object::markObjects(that);
+}
+
+
+DEFINE_MANAGED_VTABLE(FunctionCtor);
+
+FunctionCtor::FunctionCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Function")))
+{
+ vtbl = &static_vtbl;
+}
+
+// 15.3.2
+Value FunctionCtor::construct(Managed *that, Value *args, int argc)
+{
+ FunctionCtor *f = static_cast<FunctionCtor *>(that);
+ MemoryManager::GCBlocker gcBlocker(f->engine()->memoryManager);
+
+ ExecutionContext *ctx = f->engine()->current;
+ QString arguments;
+ QString body;
+ if (argc > 0) {
+ for (uint i = 0; i < argc - 1; ++i) {
+ if (i)
+ arguments += QLatin1String(", ");
+ arguments += args[i].toString(ctx)->toQString();
+ }
+ body = args[argc - 1].toString(ctx)->toQString();
+ }
+
+ QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}");
+
+ QQmlJS::Engine ee, *engine = &ee;
+ QQmlJS::Lexer lexer(engine);
+ lexer.setCode(function, 1, false);
+ QQmlJS::Parser parser(engine);
+
+ const bool parsed = parser.parseExpression();
+
+ if (!parsed)
+ f->engine()->current->throwSyntaxError(0);
+
+ using namespace QQmlJS::AST;
+ FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode());
+ ExecutionEngine *v4 = f->engine();
+ if (!fe)
+ v4->current->throwSyntaxError(0);
+
+ QQmlJS::V4IR::Module module;
+
+ QQmlJS::Codegen cg(v4->current, f->strictMode);
+ QQmlJS::V4IR::Function *irf = cg(QString(), function, fe, &module);
+
+ QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module));
+ QV4::Function *vmf = isel->vmFunction(irf);
+
+ return Value::fromObject(v4->newScriptFunction(v4->rootContext, vmf));
+}
+
+// 15.3.1: This is equivalent to new Function(...)
+Value FunctionCtor::call(Managed *that, const Value &, Value *args, int argc)
+{
+ return construct(that, args, argc);
+}
+
+FunctionPrototype::FunctionPrototype(ExecutionContext *ctx)
+ : FunctionObject(ctx)
+{
+}
+
+void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+
+ defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0));
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2);
+ defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1);
+ defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1);
+
+}
+
+Value FunctionPrototype::method_toString(SimpleCallContext *ctx)
+{
+ FunctionObject *fun = ctx->thisObject.asFunctionObject();
+ if (!fun)
+ ctx->throwTypeError();
+
+ return Value::fromString(ctx, QStringLiteral("function() { [code] }"));
+}
+
+Value FunctionPrototype::method_apply(SimpleCallContext *ctx)
+{
+ Value thisArg = ctx->argument(0);
+
+ Value arg = ctx->argument(1);
+ QVector<Value> args;
+
+ if (Object *arr = arg.asObject()) {
+ quint32 len = arr->get(ctx->engine->id_length).toUInt32();
+ for (quint32 i = 0; i < len; ++i) {
+ Value a = arr->getIndexed(i);
+ args.append(a);
+ }
+ } else if (!(arg.isUndefined() || arg.isNull())) {
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+ }
+
+ FunctionObject *o = ctx->thisObject.asFunctionObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ return o->call(thisArg, args.data(), args.size());
+}
+
+Value FunctionPrototype::method_call(SimpleCallContext *ctx)
+{
+ Value thisArg = ctx->argument(0);
+
+ QVector<Value> args(ctx->argumentCount ? ctx->argumentCount - 1 : 0);
+ if (ctx->argumentCount)
+ qCopy(ctx->arguments + 1,
+ ctx->arguments + ctx->argumentCount, args.begin());
+
+ FunctionObject *o = ctx->thisObject.asFunctionObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ return o->call(thisArg, args.data(), args.size());
+}
+
+Value FunctionPrototype::method_bind(SimpleCallContext *ctx)
+{
+ FunctionObject *target = ctx->thisObject.asFunctionObject();
+ if (!target)
+ ctx->throwTypeError();
+
+ Value boundThis = ctx->argument(0);
+ QVector<Value> boundArgs;
+ for (uint i = 1; i < ctx->argumentCount; ++i)
+ boundArgs += ctx->argument(i);
+
+
+ BoundFunction *f = ctx->engine->newBoundFunction(ctx->engine->rootContext, target, boundThis, boundArgs);
+ return Value::fromObject(f);
+}
+
+
+static Value throwTypeError(SimpleCallContext *ctx)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+DEFINE_MANAGED_VTABLE(ScriptFunction);
+
+ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function)
+ : FunctionObject(scope, function->name)
+{
+ vtbl = &static_vtbl;
+ this->function = function;
+ assert(function);
+ assert(function->code);
+
+ // global function
+ if (!scope)
+ return;
+
+ MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager);
+
+ needsActivation = function->needsActivation();
+ usesArgumentsObject = function->usesArgumentsObject;
+ strictMode = function->isStrict;
+ formalParameterCount = function->formals.size();
+ formalParameterList = function->formals.constData();
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount));
+
+ varCount = function->locals.size();
+ varList = function->locals.constData();
+
+ Object *proto = scope->engine->newObject();
+ proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this));
+ Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable);
+ pd->value = Value::fromObject(proto);
+
+ if (scope->strictMode) {
+ FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError);
+ Property pd = Property::fromAccessor(thrower, thrower);
+ __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable);
+ __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable);
+ }
+}
+
+Value ScriptFunction::construct(Managed *that, Value *args, int argc)
+{
+ ScriptFunction *f = static_cast<ScriptFunction *>(that);
+ assert(f->function->code);
+ ExecutionEngine *v4 = f->engine();
+ Object *obj = v4->newObject();
+ Value proto = f->get(v4->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+
+ ExecutionContext *context = v4->current;
+ quintptr stackSpace[stackContextSize/sizeof(quintptr)];
+ ExecutionContext *ctx = v4->newCallContext(stackSpace, f, Value::fromObject(obj), args, argc);
+
+ Value result;
+ try {
+ result = f->function->code(ctx, f->function->codeData);
+ } catch (Exception &ex) {
+ ex.partiallyUnwindContext(context);
+ throw;
+ }
+ ctx->engine->popContext();
+
+ if (result.isObject())
+ return result;
+ return Value::fromObject(obj);
+}
+
+Value ScriptFunction::call(Managed *that, const Value &thisObject, Value *args, int argc)
+{
+ ScriptFunction *f = static_cast<ScriptFunction *>(that);
+ assert(f->function->code);
+ quintptr stackSpace[stackContextSize/sizeof(quintptr)];
+ ExecutionContext *context = f->engine()->current;
+ ExecutionContext *ctx = f->engine()->newCallContext(stackSpace, f, thisObject, args, argc);
+
+ if (!f->strictMode && !thisObject.isObject()) {
+ if (thisObject.isUndefined() || thisObject.isNull()) {
+ ctx->thisObject = Value::fromObject(f->engine()->globalObject);
+ } else {
+ ctx->thisObject = Value::fromObject(thisObject.toObject(context));
+ }
+ }
+
+ Value result;
+ try {
+ result = f->function->code(ctx, f->function->codeData);
+ } catch (Exception &ex) {
+ ex.partiallyUnwindContext(context);
+ throw;
+ }
+ ctx->engine->popContext();
+ return result;
+}
+
+
+
+DEFINE_MANAGED_VTABLE(BuiltinFunctionOld);
+
+BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *))
+ : FunctionObject(scope, name)
+ , code(code)
+{
+ vtbl = &static_vtbl;
+ isBuiltinFunction = true;
+}
+
+Value BuiltinFunctionOld::construct(Managed *f, Value *, int)
+{
+ f->engine()->current->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Value BuiltinFunctionOld::call(Managed *that, const Value &thisObject, Value *args, int argc)
+{
+ BuiltinFunctionOld *f = static_cast<BuiltinFunctionOld *>(that);
+ ExecutionEngine *v4 = f->engine();
+ ExecutionContext *context = v4->current;
+
+ SimpleCallContext ctx;
+ ctx.initSimpleCallContext(f->scope->engine);
+ ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context?
+ ctx.thisObject = thisObject;
+ ctx.arguments = args;
+ ctx.argumentCount = argc;
+ v4->pushContext(&ctx);
+
+ if (!f->strictMode && !thisObject.isObject()) {
+ // Built-in functions allow for the this object to be null or undefined. This overrides
+ // the behaviour of changing thisObject to the global object if null/undefined and allows
+ // the built-in functions for example to throw a type error if null is passed.
+ if (!thisObject.isUndefined() && !thisObject.isNull())
+ ctx.thisObject = Value::fromObject(thisObject.toObject(context));
+ }
+
+ Value result = Value::undefinedValue();
+ try {
+ result = f->code(&ctx);
+ } catch (Exception &ex) {
+ ex.partiallyUnwindContext(context);
+ throw;
+ }
+
+ context->engine->popContext();
+ return result;
+}
+
+Value IndexedBuiltinFunction::call(Managed *that, const Value &thisObject, Value *args, int argc)
+{
+ IndexedBuiltinFunction *f = static_cast<IndexedBuiltinFunction *>(that);
+ ExecutionEngine *v4 = f->engine();
+ ExecutionContext *context = v4->current;
+
+ SimpleCallContext ctx;
+ ctx.initSimpleCallContext(f->scope->engine);
+ ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context?
+ ctx.thisObject = thisObject;
+ ctx.arguments = args;
+ ctx.argumentCount = argc;
+ v4->pushContext(&ctx);
+
+ if (!f->strictMode && !thisObject.isObject()) {
+ // Built-in functions allow for the this object to be null or undefined. This overrides
+ // the behaviour of changing thisObject to the global object if null/undefined and allows
+ // the built-in functions for example to throw a type error if null is passed.
+ if (!thisObject.isUndefined() && !thisObject.isNull())
+ ctx.thisObject = Value::fromObject(thisObject.toObject(context));
+ }
+
+ Value result = Value::undefinedValue();
+ try {
+ result = f->code(&ctx, f->index);
+ } catch (Exception &ex) {
+ ex.partiallyUnwindContext(context);
+ throw;
+ }
+
+ context->engine->popContext();
+ return result;
+}
+
+DEFINE_MANAGED_VTABLE(IndexedBuiltinFunction);
+
+DEFINE_MANAGED_VTABLE(BoundFunction);
+
+BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs)
+ : FunctionObject(scope, 0)
+ , target(target)
+ , boundThis(boundThis)
+ , boundArgs(boundArgs)
+{
+ vtbl = &static_vtbl;
+ int len = target->get(scope->engine->id_length).toUInt32();
+ len -= boundArgs.size();
+ if (len < 0)
+ len = 0;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len));
+
+ FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError);
+ Property pd = Property::fromAccessor(thrower, thrower);
+ *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd;
+ *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd;
+}
+
+void BoundFunction::destroy(Managed *that)
+{
+ static_cast<BoundFunction *>(that)->~BoundFunction();
+}
+
+Value BoundFunction::call(Managed *that, const Value &, Value *args, int argc)
+{
+ BoundFunction *f = static_cast<BoundFunction *>(that);
+ Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc)));
+ memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value));
+ memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value));
+
+ return f->target->call(f->boundThis, newArgs, f->boundArgs.size() + argc);
+}
+
+Value BoundFunction::construct(Managed *that, Value *args, int argc)
+{
+ BoundFunction *f = static_cast<BoundFunction *>(that);
+ Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc)));
+ memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value));
+ memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value));
+
+ return f->target->construct(newArgs, f->boundArgs.size() + argc);
+}
+
+bool BoundFunction::hasInstance(Managed *that, const Value &value)
+{
+ BoundFunction *f = static_cast<BoundFunction *>(that);
+ return FunctionObject::hasInstance(f->target, value);
+}
+
+void BoundFunction::markObjects(Managed *that)
+{
+ BoundFunction *o = static_cast<BoundFunction *>(that);
+ o->target->mark();
+ if (Managed *m = o->boundThis.asManaged())
+ m->mark();
+ for (int i = 0; i < o->boundArgs.size(); ++i)
+ if (Managed *m = o->boundArgs.at(i).asManaged())
+ m->mark();
+ FunctionObject::markObjects(that);
+}
diff --git a/src/qml/qml/v4/qv4functionobject_p.h b/src/qml/qml/v4/qv4functionobject_p.h
new file mode 100644
index 0000000000..39e07a2b8d
--- /dev/null
+++ b/src/qml/qml/v4/qv4functionobject_p.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4FUNCTIONOBJECT_H
+#define QV4FUNCTIONOBJECT_H
+
+#include "qv4global_p.h"
+#include "qv4runtime_p.h"
+#include "qv4engine_p.h"
+#include "qv4context_p.h"
+#include "qv4object_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"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+struct Object;
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct ArrayObject;
+struct DateObject;
+struct FunctionObject;
+struct ErrorObject;
+struct ArgumentsObject;
+struct ExecutionContext;
+struct ExecutionEngine;
+class MemoryManager;
+
+struct ObjectPrototype;
+struct StringPrototype;
+struct NumberPrototype;
+struct BooleanPrototype;
+struct ArrayPrototype;
+struct FunctionPrototype;
+struct DatePrototype;
+struct ErrorPrototype;
+struct EvalErrorPrototype;
+struct RangeErrorPrototype;
+struct ReferenceErrorPrototype;
+struct SyntaxErrorPrototype;
+struct TypeErrorPrototype;
+struct URIErrorPrototype;
+struct InternalClass;
+struct Lookup;
+
+struct Q_QML_EXPORT FunctionObject: Object {
+ // Used with Managed::subType
+ enum FunctionType {
+ RegularFunction = 0,
+ WrappedQtMethod = 1
+ };
+
+ ExecutionContext *scope;
+ String *name;
+ String * const *formalParameterList;
+ String * const *varList;
+ unsigned int formalParameterCount;
+ unsigned int varCount;
+ Function *function;
+
+ FunctionObject(ExecutionContext *scope, String *name = 0);
+
+ Value newInstance();
+
+ static Value construct(Managed *that, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+ inline Value construct(Value *args, int argc) {
+ return vtbl->construct(this, args, argc);
+ }
+ inline Value call(const Value &thisObject, Value *args, int argc) {
+ return vtbl->call(this, thisObject, args, argc);
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+ static void markObjects(Managed *that);
+ static bool hasInstance(Managed *that, const Value &value);
+};
+
+struct FunctionCtor: FunctionObject
+{
+ FunctionCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct FunctionPrototype: FunctionObject
+{
+ FunctionPrototype(ExecutionContext *ctx);
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_apply(SimpleCallContext *ctx);
+ static Value method_call(SimpleCallContext *ctx);
+ static Value method_bind(SimpleCallContext *ctx);
+};
+
+struct BuiltinFunctionOld: FunctionObject {
+ Value (*code)(SimpleCallContext *);
+
+ BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *));
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct IndexedBuiltinFunction: FunctionObject
+{
+ Q_MANAGED
+
+ Value (*code)(SimpleCallContext *ctx, uint index);
+ uint index;
+
+ IndexedBuiltinFunction(ExecutionContext *scope, uint index, Value (*code)(SimpleCallContext *ctx, uint index))
+ : FunctionObject(scope, /*name*/0)
+ , code(code)
+ , index(index)
+ {
+ vtbl = &static_vtbl;
+ isBuiltinFunction = true;
+ }
+
+ static Value construct(Managed *m, Value *, int)
+ {
+ m->engine()->current->throwTypeError();
+ return Value::undefinedValue();
+ }
+
+ static Value call(Managed *that, const Value &thisObject, Value *args, int argc);
+};
+
+
+struct ScriptFunction: FunctionObject {
+ ScriptFunction(ExecutionContext *scope, Function *function);
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct BoundFunction: FunctionObject {
+ FunctionObject *target;
+ Value boundThis;
+ QVector<Value> boundArgs;
+
+ BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
+ ~BoundFunction() {}
+
+
+ static Value construct(Managed *, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *);
+ static void markObjects(Managed *that);
+ static bool hasInstance(Managed *that, const Value &value);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4/qv4global_p.h b/src/qml/qml/v4/qv4global_p.h
new file mode 100644
index 0000000000..8e47f3c88a
--- /dev/null
+++ b/src/qml/qml/v4/qv4global_p.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GLOBAL_H
+#define QV4GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#include <qtqmlglobal.h>
+
+#if defined(Q_CC_MSVC)
+namespace std {
+
+inline bool isinf(double d) { return !_finite(d) && !_isnan(d); }
+inline bool isnan(double d) { return !!_isnan(d); }
+inline bool isfinite(double d) { return _finite(d); }
+inline bool signbit(double d) { return _copysign(1.0, d) < 0; }
+
+} // namespace std
+
+inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class MemoryManager;
+struct String;
+struct Object;
+struct ObjectPrototype;
+struct ObjectIterator;
+struct ExecutionContext;
+struct ScriptFunction;
+struct InternalClass;
+struct Property;
+
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct ArrayObject;
+struct DateObject;
+struct FunctionObject;
+struct ErrorObject;
+struct ArgumentsObject;
+struct Managed;
+struct Lookup;
+struct ExecutionEngine;
+struct QObjectWrapper;
+
+
+enum PropertyFlag {
+ Attr_Data = 0,
+ Attr_Accessor = 0x1,
+ Attr_NotWritable = 0x2,
+ Attr_NotEnumerable = 0x4,
+ Attr_NotConfigurable = 0x8,
+ Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable,
+ Attr_Invalid = 0xff
+};
+
+Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag);
+Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags);
+
+struct PropertyAttributes
+{
+ union {
+ uchar m_all;
+ struct {
+ uchar m_flags : 4;
+ uchar m_mask : 4;
+ };
+ struct {
+ uchar m_type : 1;
+ uchar m_writable : 1;
+ uchar m_enumerable : 1;
+ uchar m_configurable : 1;
+ uchar type_set : 1;
+ uchar writable_set : 1;
+ uchar enumerable_set : 1;
+ uchar configurable_set : 1;
+ };
+ };
+
+ enum Type {
+ Data = 0,
+ Accessor = 1,
+ Generic = 2
+ };
+
+ PropertyAttributes() : m_all(0) {}
+ PropertyAttributes(PropertyFlag f) : m_all(0) {
+ if (f != Attr_Invalid) {
+ setType(f & Attr_Accessor ? Accessor : Data);
+ if (!(f & Attr_Accessor))
+ setWritable(!(f & Attr_NotWritable));
+ setEnumerable(!(f & Attr_NotEnumerable));
+ setConfigurable(!(f & Attr_NotConfigurable));
+ }
+ }
+ PropertyAttributes(PropertyFlags f) : m_all(0) {
+ if (f != Attr_Invalid) {
+ setType(f & Attr_Accessor ? Accessor : Data);
+ if (!(f & Attr_Accessor))
+ setWritable(!(f & Attr_NotWritable));
+ setEnumerable(!(f & Attr_NotEnumerable));
+ setConfigurable(!(f & Attr_NotConfigurable));
+ }
+ }
+ PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {}
+ PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; }
+
+ void setType(Type t) { m_type = t; type_set = true; }
+ Type type() const { return type_set ? (Type)m_type : Generic; }
+
+ bool isData() const { return type() == PropertyAttributes::Data || writable_set; }
+ bool isAccessor() const { return type() == PropertyAttributes::Accessor; }
+ bool isGeneric() const { return type() == PropertyAttributes::Generic && !writable_set; }
+
+ bool hasType() const { return type_set; }
+ bool hasWritable() const { return writable_set; }
+ bool hasConfigurable() const { return configurable_set; }
+ bool hasEnumerable() const { return enumerable_set; }
+
+ void setWritable(bool b) { m_writable = b; writable_set = true; }
+ void setConfigurable(bool b) { m_configurable = b; configurable_set = true; }
+ void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; }
+
+ void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } }
+
+ bool isWritable() const { return m_type != Data || m_writable; }
+ bool isEnumerable() const { return m_enumerable; }
+ bool isConfigurable() const { return m_configurable; }
+
+ void clearType() { m_type = Data; type_set = false; }
+ void clearWritable() { m_writable = false; writable_set = false; }
+ void clearEnumerable() { m_enumerable = false; enumerable_set = false; }
+ void clearConfigurable() { m_configurable = false; configurable_set = false; }
+
+ void clear() { m_all = 0; }
+ bool isEmpty() const { return !m_all; }
+
+ uint flags() const { return m_flags; }
+
+ bool operator==(PropertyAttributes other) {
+ return m_all == other.m_all;
+ }
+ bool operator!=(PropertyAttributes other) {
+ return m_all != other.m_all;
+ }
+};
+
+}
+
+Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QV4GLOBAL_H
diff --git a/src/qml/qml/v4/qv4globalobject.cpp b/src/qml/qml/v4/qv4globalobject.cpp
new file mode 100644
index 0000000000..6b279416a3
--- /dev/null
+++ b/src/qml/qml/v4/qv4globalobject.cpp
@@ -0,0 +1,652 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4globalobject_p.h"
+#include "qv4mm_p.h"
+#include "qv4value_p.h"
+#include "qv4context_p.h"
+#include "qv4function_p.h"
+#include "qv4debugging_p.h"
+#include "qv4script_p.h"
+#include "qv4exception_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>
+#include <QtCore/QString>
+#include <iostream>
+#include "qv4alloca_p.h"
+
+#include <wtf/MathExtras.h>
+
+using namespace QV4;
+
+
+static inline char toHex(char c)
+{
+ static const char hexnumbers[] = "0123456789ABCDEF";
+ return hexnumbers[c & 0xf];
+}
+
+static int fromHex(QChar ch)
+{
+ ushort c = ch.unicode();
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+ return -1;
+}
+
+static QString escape(const QString &input)
+{
+ QString output;
+ output.reserve(input.size() * 3);
+ const int length = input.length();
+ for (int i = 0; i < length; ++i) {
+ ushort uc = input.at(i).unicode();
+ if (uc < 0x100) {
+ if ( (uc > 0x60 && uc < 0x7B)
+ || (uc > 0x3F && uc < 0x5B)
+ || (uc > 0x2C && uc < 0x3A)
+ || (uc == 0x2A)
+ || (uc == 0x2B)
+ || (uc == 0x5F)) {
+ output.append(QChar(uc));
+ } else {
+ output.append('%');
+ output.append(QChar(toHex(uc >> 4)));
+ output.append(QChar(toHex(uc)));
+ }
+ } else {
+ output.append('%');
+ output.append('u');
+ output.append(QChar(toHex(uc >> 12)));
+ output.append(QChar(toHex(uc >> 8)));
+ output.append(QChar(toHex(uc >> 4)));
+ output.append(QChar(toHex(uc)));
+ }
+ }
+ return output;
+}
+
+static QString unescape(const QString &input)
+{
+ QString result;
+ result.reserve(input.length());
+ int i = 0;
+ const int length = input.length();
+ while (i < length) {
+ QChar c = input.at(i++);
+ if ((c == '%') && (i + 1 < length)) {
+ QChar a = input.at(i);
+ if ((a == 'u') && (i + 4 < length)) {
+ int d3 = fromHex(input.at(i+1));
+ int d2 = fromHex(input.at(i+2));
+ int d1 = fromHex(input.at(i+3));
+ int d0 = fromHex(input.at(i+4));
+ if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) {
+ ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0);
+ result.append(QChar(uc));
+ i += 5;
+ } else {
+ result.append(c);
+ }
+ } else {
+ int d1 = fromHex(a);
+ int d0 = fromHex(input.at(i+1));
+ if ((d1 != -1) && (d0 != -1)) {
+ c = (d1 << 4) | d0;
+ i += 2;
+ }
+ result.append(c);
+ }
+ } else {
+ result.append(c);
+ }
+ }
+ return result;
+}
+
+static const char uriReserved[] = ";/?:@&=+$,#";
+static const char uriUnescaped[] = "-_.!~*'()";
+static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#";
+
+static void addEscapeSequence(QString &output, uchar ch)
+{
+ output.append(QLatin1Char('%'));
+ output.append(QLatin1Char(toHex(ch >> 4)));
+ output.append(QLatin1Char(toHex(ch & 0xf)));
+}
+
+static QString encode(const QString &input, const char *unescapedSet, bool *ok)
+{
+ *ok = true;
+ QString output;
+ const int length = input.length();
+ int i = 0;
+ while (i < length) {
+ const QChar c = input.at(i);
+ bool escape = true;
+ if ((c.unicode() >= 'a' && c.unicode() <= 'z') ||
+ (c.unicode() >= 'A' && c.unicode() <= 'Z') ||
+ (c.unicode() >= '0' && c.unicode() <= '9')) {
+ escape = false;
+ } else {
+ const char *r = unescapedSet;
+ while (*r) {
+ if (*r == c.unicode()) {
+ escape = false;
+ break;
+ }
+ ++r;
+ }
+ }
+ if (escape) {
+ uint uc = c.unicode();
+ if ((uc >= 0xDC00) && (uc <= 0xDFFF)) {
+ *ok = false;
+ break;
+ }
+ if (!((uc < 0xD800) || (uc > 0xDBFF))) {
+ ++i;
+ if (i == length) {
+ *ok = false;
+ break;
+ }
+ const uint uc2 = input.at(i).unicode();
+ if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) {
+ *ok = false;
+ break;
+ }
+ uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000;
+ }
+ if (uc < 0x80) {
+ addEscapeSequence(output, (uchar)uc);
+ } else {
+ if (uc < 0x0800) {
+ addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6)));
+ } else {
+
+ if (QChar::requiresSurrogates(uc)) {
+ addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18)));
+ addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f));
+ } else {
+ addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f));
+ }
+ addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f));
+ }
+ addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f)));
+ }
+ } else {
+ output.append(c);
+ }
+ ++i;
+ }
+ if (i != length)
+ *ok = false;
+ return output;
+}
+
+enum DecodeMode {
+ DecodeAll,
+ DecodeNonReserved
+};
+
+static QString decode(const QString &input, DecodeMode decodeMode, bool *ok)
+{
+ *ok = true;
+ QString output;
+ output.reserve(input.length());
+ const int length = input.length();
+ int i = 0;
+ const QChar percent = QLatin1Char('%');
+ while (i < length) {
+ const QChar ch = input.at(i);
+ if (ch == percent) {
+ int start = i;
+ if (i + 2 >= length)
+ goto error;
+
+ int d1 = fromHex(input.at(i+1));
+ int d0 = fromHex(input.at(i+2));
+ if ((d1 == -1) || (d0 == -1))
+ goto error;
+
+ int b = (d1 << 4) | d0;
+ i += 2;
+ if (b & 0x80) {
+ int uc;
+ int min_uc;
+ int need;
+ if ((b & 0xe0) == 0xc0) {
+ uc = b & 0x1f;
+ need = 1;
+ min_uc = 0x80;
+ } else if ((b & 0xf0) == 0xe0) {
+ uc = b & 0x0f;
+ need = 2;
+ min_uc = 0x800;
+ } else if ((b & 0xf8) == 0xf0) {
+ uc = b & 0x07;
+ need = 3;
+ min_uc = 0x10000;
+ } else {
+ goto error;
+ }
+
+ if (i + (3 * need) >= length)
+ goto error;
+
+ for (int j = 0; j < need; ++j) {
+ ++i;
+ if (input.at(i) != percent)
+ goto error;
+
+ d1 = fromHex(input.at(i+1));
+ d0 = fromHex(input.at(i+2));
+ if ((d1 == -1) || (d0 == -1))
+ goto error;
+
+ b = (d1 << 4) | d0;
+ if ((b & 0xC0) != 0x80)
+ goto error;
+
+ i += 2;
+ uc = (uc << 6) + (b & 0x3f);
+ }
+ if (uc < min_uc)
+ goto error;
+
+ if (uc < 0x10000) {
+ output.append(QChar(uc));
+ } else {
+ if (uc > 0x10FFFF)
+ goto error;
+
+ ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00);
+ ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ output.append(QChar(h));
+ output.append(QChar(l));
+ }
+ } else {
+ if (decodeMode == DecodeNonReserved && b <= 0x40) {
+ const char *r = uriReserved;
+ while (*r) {
+ if (*r == b)
+ break;
+ ++r;
+ }
+ if (*r)
+ output.append(input.mid(start, i - start + 1));
+ else
+ output.append(QChar(b));
+ } else {
+ output.append(QChar(b));
+ }
+ }
+ } else {
+ output.append(ch);
+ }
+ ++i;
+ }
+ if (i != length)
+ *ok = false;
+ return output;
+ error:
+ *ok = false;
+ return QString();
+}
+
+DEFINE_MANAGED_VTABLE(EvalFunction);
+
+EvalFunction::EvalFunction(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->id_eval)
+{
+ vtbl = &static_vtbl;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
+}
+
+Value EvalFunction::evalCall(Value /*thisObject*/, Value *args, int argc, bool directCall)
+{
+ if (argc < 1)
+ return Value::undefinedValue();
+
+ ExecutionContext *parentContext = engine()->current;
+ ExecutionEngine *engine = parentContext->engine;
+ ExecutionContext *ctx = parentContext;
+
+ if (!directCall) {
+ // the context for eval should be the global scope, so we fake a root
+ // context
+ ctx = engine->pushGlobalContext();
+ }
+
+ if (!args[0].isString())
+ return args[0];
+
+ const QString code = args[0].stringValue()->toQString();
+ bool inheritContext = !ctx->strictMode;
+
+ Script script(ctx, code, QString("eval code"));
+ script.strictMode = (directCall && parentContext->strictMode);
+ script.inheritContext = inheritContext;
+ script.parse();
+
+ Function *function = script.function();
+ if (!function)
+ return Value::undefinedValue();
+
+ strictMode = function->isStrict || (ctx->strictMode);
+
+ usesArgumentsObject = function->usesArgumentsObject;
+ needsActivation = function->needsActivation();
+
+ if (strictMode) {
+ FunctionObject *e = engine->newScriptFunction(ctx, function);
+ return e->call(ctx->thisObject, 0, 0);
+ }
+
+ ExecutionContext::EvalCode evalCode;
+ evalCode.function = function;
+ evalCode.next = ctx->currentEvalCode;
+ ctx->currentEvalCode = &evalCode;
+
+ // set the correct strict mode flag on the context
+ bool cstrict = ctx->strictMode;
+ ctx->strictMode = strictMode;
+
+ Value result = Value::undefinedValue();
+ try {
+ result = function->code(ctx, function->codeData);
+ } catch (Exception &ex) {
+ ctx->strictMode = cstrict;
+ ctx->currentEvalCode = evalCode.next;
+ if (strictMode)
+ ex.partiallyUnwindContext(parentContext);
+ throw;
+ }
+
+ ctx->strictMode = cstrict;
+ ctx->currentEvalCode = evalCode.next;
+
+ while (engine->current != parentContext)
+ engine->popContext();
+
+ return result;
+}
+
+
+Value EvalFunction::call(Managed *that, const Value &thisObject, Value *args, int argc)
+{
+ // indirect call
+ return static_cast<EvalFunction *>(that)->evalCall(thisObject, args, argc, false);
+}
+
+
+static inline int toInt(const QChar &qc, int R)
+{
+ ushort c = qc.unicode();
+ int v = -1;
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ v = c - 'A' + 10;
+ else if (c >= 'a' && c <= 'z')
+ v = c - 'a' + 10;
+ if (v >= 0 && v < R)
+ return v;
+ else
+ return -1;
+}
+
+// parseInt [15.1.2.2]
+Value GlobalFunctions::method_parseInt(SimpleCallContext *context)
+{
+ Value string = context->argument(0);
+ Value radix = context->argument(1);
+ int R = radix.isUndefined() ? 0 : radix.toInt32();
+
+ // [15.1.2.2] step by step:
+ String *inputString = string.toString(context); // 1
+ QString trimmed = inputString->toQString().trimmed(); // 2
+ const QChar *pos = trimmed.constData();
+ const QChar *end = pos + trimmed.length();
+
+ int sign = 1; // 3
+ if (pos != end) {
+ if (*pos == QLatin1Char('-'))
+ sign = -1; // 4
+ if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+'))
+ ++pos; // 5
+ }
+ bool stripPrefix = true; // 7
+ if (R) { // 8
+ if (R < 2 || R > 36)
+ return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 8a
+ if (R != 16)
+ stripPrefix = false; // 8b
+ } else { // 9
+ R = 10; // 9a
+ }
+ if (stripPrefix) { // 10
+ if ((end - pos >= 2)
+ && (pos[0] == QLatin1Char('0'))
+ && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a
+ pos += 2;
+ R = 16;
+ }
+ }
+ // 11: Z is progressively built below
+ // 13: this is handled by the toInt function
+ if (pos == end) // 12
+ return Value::fromDouble(std::numeric_limits<double>::quiet_NaN());
+ bool overflow = false;
+ qint64 v_overflow;
+ unsigned overflow_digit_count = 0;
+ int d = toInt(*pos++, R);
+ if (d == -1)
+ return Value::fromDouble(std::numeric_limits<double>::quiet_NaN());
+ qint64 v = d;
+ while (pos != end) {
+ d = toInt(*pos++, R);
+ if (d == -1)
+ break;
+ if (overflow) {
+ if (overflow_digit_count == 0) {
+ v_overflow = v;
+ v = 0;
+ }
+ ++overflow_digit_count;
+ v = v * R + d;
+ } else {
+ qint64 vNew = v * R + d;
+ if (vNew < v) {
+ overflow = true;
+ --pos;
+ } else {
+ v = vNew;
+ }
+ }
+ }
+
+ if (overflow) {
+ double result = (double) v_overflow * pow(R, overflow_digit_count);
+ result += v;
+ return Value::fromDouble(sign * result);
+ } else {
+ return Value::fromDouble(sign * (double) v); // 15
+ }
+}
+
+// parseFloat [15.1.2.3]
+Value GlobalFunctions::method_parseFloat(SimpleCallContext *context)
+{
+ Value string = context->argument(0);
+
+ // [15.1.2.3] step by step:
+ String *inputString = string.toString(context); // 1
+ QString trimmed = inputString->toQString().trimmed(); // 2
+
+ // 4:
+ if (trimmed.startsWith(QLatin1String("Infinity"))
+ || trimmed.startsWith(QLatin1String("+Infinity")))
+ return Value::fromDouble(Q_INFINITY);
+ if (trimmed.startsWith("-Infinity"))
+ return Value::fromDouble(-Q_INFINITY);
+ QByteArray ba = trimmed.toLatin1();
+ bool ok;
+ const char *begin = ba.constData();
+ const char *end = 0;
+ double d = qstrtod(begin, &end, &ok);
+ if (end - begin == 0)
+ return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 3
+ else
+ return Value::fromDouble(d);
+}
+
+/// isNaN [15.1.2.4]
+Value GlobalFunctions::method_isNaN(SimpleCallContext *context)
+{
+ const Value &v = context->argument(0);
+ if (v.integerCompatible())
+ return Value::fromBoolean(false);
+
+ double d = v.toNumber();
+ return Value::fromBoolean(std::isnan(d));
+}
+
+/// isFinite [15.1.2.5]
+Value GlobalFunctions::method_isFinite(SimpleCallContext *context)
+{
+ const Value &v = context->argument(0);
+ if (v.integerCompatible())
+ return Value::fromBoolean(true);
+
+ double d = v.toNumber();
+ return Value::fromBoolean(std::isfinite(d));
+}
+
+/// decodeURI [15.1.3.1]
+Value GlobalFunctions::method_decodeURI(SimpleCallContext *context)
+{
+ if (context->argumentCount == 0)
+ return Value::undefinedValue();
+
+ QString uriString = context->arguments[0].toString(context)->toQString();
+ bool ok;
+ QString out = decode(uriString, DecodeNonReserved, &ok);
+ if (!ok)
+ context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(context, out);
+}
+
+/// decodeURIComponent [15.1.3.2]
+Value GlobalFunctions::method_decodeURIComponent(SimpleCallContext *context)
+{
+ if (context->argumentCount == 0)
+ return Value::undefinedValue();
+
+ QString uriString = context->arguments[0].toString(context)->toQString();
+ bool ok;
+ QString out = decode(uriString, DecodeAll, &ok);
+ if (!ok)
+ context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(context, out);
+}
+
+/// encodeURI [15.1.3.3]
+Value GlobalFunctions::method_encodeURI(SimpleCallContext *context)
+{
+ if (context->argumentCount == 0)
+ return Value::undefinedValue();
+
+ QString uriString = context->arguments[0].toString(context)->toQString();
+ bool ok;
+ QString out = encode(uriString, uriUnescapedReserved, &ok);
+ if (!ok)
+ context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(context, out);
+}
+
+/// encodeURIComponent [15.1.3.4]
+Value GlobalFunctions::method_encodeURIComponent(SimpleCallContext *context)
+{
+ if (context->argumentCount == 0)
+ return Value::undefinedValue();
+
+ QString uriString = context->arguments[0].toString(context)->toQString();
+ bool ok;
+ QString out = encode(uriString, uriUnescaped, &ok);
+ if (!ok)
+ context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(context, out);
+}
+
+Value GlobalFunctions::method_escape(SimpleCallContext *context)
+{
+ if (!context->argumentCount)
+ return Value::fromString(context, QStringLiteral("undefined"));
+
+ QString str = context->argument(0).toString(context)->toQString();
+ return Value::fromString(context, escape(str));
+}
+
+Value GlobalFunctions::method_unescape(SimpleCallContext *context)
+{
+ if (!context->argumentCount)
+ return Value::fromString(context, QStringLiteral("undefined"));
+
+ QString str = context->argument(0).toString(context)->toQString();
+ return Value::fromString(context, unescape(str));
+}
diff --git a/src/qml/qml/v4/qv4globalobject_p.h b/src/qml/qml/v4/qv4globalobject_p.h
new file mode 100644
index 0000000000..11d034b5b4
--- /dev/null
+++ b/src/qml/qml/v4/qv4globalobject_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4GLOBALOBJECT_H
+#define QV4GLOBALOBJECT_H
+
+#include "qv4global_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Q_QML_EXPORT EvalFunction : FunctionObject
+{
+ EvalFunction(ExecutionContext *scope);
+
+ Value evalCall(Value thisObject, Value *args, int argc, bool directCall);
+
+ using Managed::construct;
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct GlobalFunctions
+{
+ static Value method_parseInt(SimpleCallContext *context);
+ static Value method_parseFloat(SimpleCallContext *context);
+ static Value method_isNaN(SimpleCallContext *context);
+ static Value method_isFinite(SimpleCallContext *context);
+ static Value method_decodeURI(SimpleCallContext *context);
+ static Value method_decodeURIComponent(SimpleCallContext *context);
+ static Value method_encodeURI(SimpleCallContext *context);
+ static Value method_encodeURIComponent(SimpleCallContext *context);
+ static Value method_escape(SimpleCallContext *context);
+ static Value method_unescape(SimpleCallContext *context);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4/qv4identifier.cpp b/src/qml/qml/v4/qv4identifier.cpp
new file mode 100644
index 0000000000..5d8077bfdc
--- /dev/null
+++ b/src/qml/qml/v4/qv4identifier.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4identifier_p.h"
+#include "qv4identifiertable_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+static const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
+ 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
+};
+
+static inline int primeForNumBits(int numBits)
+{
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+
+IdentifierHashData::IdentifierHashData(int numBits)
+ : numBits(numBits)
+ , size(0)
+{
+ refCount.store(1);
+ alloc = primeForNumBits(numBits);
+ entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
+ memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
+}
+
+IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine)
+{
+ d = new IdentifierHashData(3);
+ d->identifierTable = engine->identifierTable;
+}
+
+
+IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier)
+{
+ // fill up to max 50%
+ bool grow = (d->alloc <= d->size*2);
+
+ if (grow) {
+ ++d->numBits;
+ int newAlloc = primeForNumBits(d->numBits);
+ IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry));
+ memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry));
+ for (uint i = 0; i < d->alloc; ++i) {
+ const IdentifierHashEntry &e = d->entries[i];
+ if (!e.identifier)
+ continue;
+ uint idx = e.identifier->hashValue % newAlloc;
+ while (newEntries[idx].identifier) {
+ ++idx;
+ idx %= newAlloc;
+ }
+ newEntries[idx] = e;
+ }
+ free(d->entries);
+ d->entries = newEntries;
+ d->alloc = newAlloc;
+ }
+
+ uint idx = identifier->hashValue % d->alloc;
+ while (d->entries[idx].identifier) {
+ Q_ASSERT(d->entries[idx].identifier != identifier);
+ ++idx;
+ idx %= d->alloc;
+ }
+ d->entries[idx].identifier = identifier;
+ ++d->size;
+ return d->entries + idx;
+}
+
+const IdentifierHashEntry *IdentifierHashBase::lookup(const Identifier *identifier) const
+{
+ if (!d)
+ return 0;
+ assert(d->entries);
+
+ uint idx = identifier->hashValue % d->alloc;
+ while (1) {
+ if (!d->entries[idx].identifier)
+ return 0;
+ if (d->entries[idx].identifier == identifier)
+ return d->entries + idx;
+ ++idx;
+ idx %= d->alloc;
+ }
+}
+
+const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const
+{
+ if (!d)
+ return 0;
+ assert(d->entries);
+
+ uint hash = String::createHashValue(str.constData(), str.length());
+ uint idx = hash % d->alloc;
+ while (1) {
+ if (!d->entries[idx].identifier)
+ return 0;
+ if (d->entries[idx].identifier->string == str)
+ return d->entries + idx;
+ ++idx;
+ idx %= d->alloc;
+ }
+}
+
+const IdentifierHashEntry *IdentifierHashBase::lookup(String *str) const
+{
+ if (!d)
+ return 0;
+ if (str->identifier)
+ return lookup(str->identifier);
+ return lookup(str->toQString());
+}
+
+const Identifier *IdentifierHashBase::toIdentifier(const QString &str) const
+{
+ Q_ASSERT(d);
+ return d->identifierTable->identifier(str);
+}
+
+const Identifier *IdentifierHashBase::toIdentifier(String *str) const
+{
+ Q_ASSERT(d);
+ return d->identifierTable->identifier(str);
+}
+
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4identifier_p.h b/src/qml/qml/v4/qv4identifier_p.h
new file mode 100644
index 0000000000..7c69e1d8c4
--- /dev/null
+++ b/src/qml/qml/v4/qv4identifier_p.h
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4IDENTIFIER_H
+#define QV4IDENTIFIER_H
+
+#include <qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct String;
+struct IdentifierTable;
+struct ExecutionEngine;
+
+struct Identifier
+{
+ QString string;
+ uint hashValue;
+};
+
+
+struct IdentifierHashEntry {
+ const Identifier *identifier;
+ union {
+ int value;
+ void *pointer;
+ };
+ int get(int *) const { return this ? value : -1; }
+ bool get(bool *) const { return this != 0; }
+ void *get(void **) const { return this ? pointer : 0; }
+};
+
+struct IdentifierHashData
+{
+ IdentifierHashData(int numBits);
+ ~IdentifierHashData() {
+ free(entries);
+ }
+
+ QBasicAtomicInt refCount;
+ int alloc;
+ int size;
+ int numBits;
+ IdentifierTable *identifierTable;
+ IdentifierHashEntry *entries;
+};
+
+struct IdentifierHashBase
+{
+
+ IdentifierHashData *d;
+
+ IdentifierHashBase() : d(0) {}
+ IdentifierHashBase(ExecutionEngine *engine);
+ inline IdentifierHashBase(const IdentifierHashBase &other);
+ inline ~IdentifierHashBase();
+ inline IdentifierHashBase &operator=(const IdentifierHashBase &other);
+
+ bool isEmpty() const { return !d; }
+ // ###
+ void reserve(int) {}
+
+ inline int count() const;
+ bool contains(const Identifier *i) const;
+ bool contains(const QString &str) const;
+ bool contains(String *str) const;
+
+protected:
+ IdentifierHashEntry *addEntry(const Identifier *i);
+ const IdentifierHashEntry *lookup(const Identifier *identifier) const;
+ const IdentifierHashEntry *lookup(const QString &str) const;
+ const IdentifierHashEntry *lookup(String *str) const;
+ const Identifier *toIdentifier(const QString &str) const;
+ const Identifier *toIdentifier(String *str) const;
+};
+
+
+template<typename T>
+struct IdentifierHash : public IdentifierHashBase
+{
+ IdentifierHash()
+ : IdentifierHashBase() {}
+ IdentifierHash(ExecutionEngine *engine)
+ : IdentifierHashBase(engine) {}
+ inline IdentifierHash(const IdentifierHash<T> &other)
+ : IdentifierHashBase(other) {}
+ inline ~IdentifierHash() {}
+ inline IdentifierHash &operator=(const IdentifierHash<T> &other) {
+ IdentifierHashBase::operator =(other);
+ return *this;
+ }
+
+ void add(const QString &str, const T &value);
+
+ inline T value(const QString &str) const;
+ inline T value(String *str) const;
+ QString findId(T value) const;
+};
+
+inline IdentifierHashBase::IdentifierHashBase(const IdentifierHashBase &other)
+{
+ d = other.d;
+ if (d)
+ d->refCount.ref();
+}
+
+inline IdentifierHashBase::~IdentifierHashBase()
+{
+ if (d && !d->refCount.deref())
+ delete d;
+}
+
+IdentifierHashBase &IdentifierHashBase::operator=(const IdentifierHashBase &other)
+{
+ if (other.d)
+ other.d->refCount.ref();
+ if (d && !d->refCount.deref())
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+inline int IdentifierHashBase::count() const
+{
+ return d ? d->size : 0;
+}
+
+inline bool IdentifierHashBase::contains(const Identifier *i) const
+{
+ return lookup(i) != 0;
+}
+
+inline bool IdentifierHashBase::contains(const QString &str) const
+{
+ return lookup(str) != 0;
+}
+
+inline bool IdentifierHashBase::contains(String *str) const
+{
+ return lookup(str) != 0;
+}
+
+template<typename T>
+void IdentifierHash<T>::add(const QString &str, const T &value)
+{
+ IdentifierHashEntry *e = addEntry(toIdentifier(str));
+ e->value = value;
+}
+
+template<typename T>
+inline T IdentifierHash<T>::value(const QString &str) const
+{
+ return lookup(str)->get((T*)0);
+}
+
+template<typename T>
+inline T IdentifierHash<T>::value(String *str) const
+{
+ return lookup(str)->get((T*)0);
+}
+
+
+template<typename T>
+QString IdentifierHash<T>::findId(T value) const
+{
+ IdentifierHashEntry *e = d->entries;
+ IdentifierHashEntry *end = e + d->alloc;
+ while (e < end) {
+ if (e->identifier && e->get((T*)0) == value)
+ return e->identifier->string;
+ ++e;
+ }
+ return QString();
+}
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4identifiertable.cpp b/src/qml/qml/v4/qv4identifiertable.cpp
new file mode 100644
index 0000000000..5de2f893ef
--- /dev/null
+++ b/src/qml/qml/v4/qv4identifiertable.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4identifiertable_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+static const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
+ 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
+};
+
+static inline int primeForNumBits(int numBits)
+{
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+
+IdentifierTable::IdentifierTable(ExecutionEngine *engine)
+ : engine(engine)
+ , size(0)
+ , numBits(8)
+{
+ alloc = primeForNumBits(numBits);
+ entries = (String **)malloc(alloc*sizeof(String *));
+ memset(entries, 0, alloc*sizeof(String *));
+}
+
+IdentifierTable::~IdentifierTable()
+{
+ free(entries);
+}
+
+void IdentifierTable::addEntry(String *str)
+{
+ uint hash = str->hashValue();
+
+ if (str->subtype == String::StringType_ArrayIndex)
+ return;
+
+ str->identifier = new Identifier;
+ str->identifier->string = str->toQString();
+ str->identifier->hashValue = hash;
+
+ bool grow = (alloc <= size*2);
+
+ if (grow) {
+ ++numBits;
+ int newAlloc = primeForNumBits(numBits);
+ String **newEntries = (String **)malloc(newAlloc*sizeof(String *));
+ memset(newEntries, 0, newAlloc*sizeof(String *));
+ for (uint i = 0; i < alloc; ++i) {
+ String *e = entries[i];
+ if (!e)
+ continue;
+ uint idx = e->stringHash % newAlloc;
+ while (newEntries[idx]) {
+ ++idx;
+ idx %= newAlloc;
+ }
+ newEntries[idx] = e;
+ }
+ free(entries);
+ entries = newEntries;
+ alloc = newAlloc;
+ }
+
+ uint idx = hash % alloc;
+ while (entries[idx]) {
+ ++idx;
+ idx %= alloc;
+ }
+ entries[idx] = str;
+ ++size;
+}
+
+
+
+String *IdentifierTable::insertString(const QString &s)
+{
+ uint hash = String::createHashValue(s.constData(), s.length());
+ uint idx = hash % alloc;
+ while (String *e = entries[idx]) {
+ if (e->stringHash == hash && e->toQString() == s)
+ return e;
+ ++idx;
+ idx %= alloc;
+ }
+
+ String *str = engine->newString(s);
+ addEntry(str);
+ return str;
+}
+
+
+Identifier *IdentifierTable::identifier(String *str)
+{
+ if (str->identifier)
+ return str->identifier;
+ uint hash = str->hashValue();
+ if (str->subtype == String::StringType_ArrayIndex)
+ return 0;
+
+ uint idx = hash % alloc;
+ while (String *e = entries[idx]) {
+ if (e->stringHash == hash && e->isEqualTo(str)) {
+ str->identifier = e->identifier;
+ return e->identifier;
+ }
+ ++idx;
+ idx %= alloc;
+ }
+
+ addEntry(str);
+ return str->identifier;
+}
+
+Identifier *IdentifierTable::identifier(const QString &s)
+{
+ return insertString(s)->identifier;
+}
+
+Identifier *IdentifierTable::identifier(const char *s, int len)
+{
+ uint hash = String::createHashValue(s, len);
+ if (hash == UINT_MAX)
+ return identifier(QString::fromUtf8(s, len));
+
+ QLatin1String latin(s, len);
+ uint idx = hash % alloc;
+ while (String *e = entries[idx]) {
+ if (e->stringHash == hash && e->toQString() == latin)
+ return e->identifier;
+ ++idx;
+ idx %= alloc;
+ }
+
+ String *str = engine->newString(QString::fromLatin1(s, len));
+ addEntry(str);
+ return str->identifier;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4identifiertable_p.h b/src/qml/qml/v4/qv4identifiertable_p.h
new file mode 100644
index 0000000000..0f9a5921f9
--- /dev/null
+++ b/src/qml/qml/v4/qv4identifiertable_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4IDENTIFIERTABLE_H
+#define QV4IDENTIFIERTABLE_H
+
+#include "qv4identifier_p.h"
+#include "qv4string_p.h"
+#include "qv4engine_p.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct IdentifierTable
+{
+ ExecutionEngine *engine;
+
+ int alloc;
+ int size;
+ int numBits;
+ String **entries;
+
+ void addEntry(String *str);
+
+public:
+
+ IdentifierTable(ExecutionEngine *engine);
+ ~IdentifierTable();
+
+ String *insertString(const QString &s);
+
+ Identifier *identifier(String *str);
+ Identifier *identifier(const QString &s);
+ Identifier *identifier(const char *s, int len);
+
+ void mark() {
+ for (int i = 0; i < alloc; ++i)
+ if (entries[i])
+ entries[i]->mark();
+ }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4include.cpp b/src/qml/qml/v4/qv4include.cpp
new file mode 100644
index 0000000000..4fd7bb14c7
--- /dev/null
+++ b/src/qml/qml/v4/qv4include.cpp
@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4include_p.h"
+
+#include <QtQml/qjsengine.h>
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtCore/qfile.h>
+#include <QtQml/qqmlfile.h>
+
+#include <private/qqmlengine_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4context_p.h>
+#include <private/qv4exception_p.h>
+#include <private/qqmlcontextwrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QV4Include::QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context,
+ const QV4::Value &qmlglobal, const QV4::Value &callback)
+ : v4(QV8Engine::getV4(engine)), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context)
+{
+ m_qmlglobal = qmlglobal;
+ if (callback.asFunctionObject())
+ m_callbackFunction = callback;
+
+ m_resultObject = resultValue(v4);
+
+ m_network = engine->networkAccessManager();
+
+ QNetworkRequest request;
+ request.setUrl(url);
+
+ m_reply = m_network->get(request);
+ QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
+}
+
+QV4Include::~QV4Include()
+{
+ delete m_reply; m_reply = 0;
+}
+
+QV4::Value QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status)
+{
+
+ // XXX It seems inefficient to create this object from scratch each time.
+ QV4::Object *o = v4->newObject();
+ o->put(v4->newString("OK"), QV4::Value::fromInt32(Ok));
+ o->put(v4->newString("LOADING"), QV4::Value::fromInt32(Loading));
+ o->put(v4->newString("NETWORK_ERROR"), QV4::Value::fromInt32(NetworkError));
+ o->put(v4->newString("EXCEPTION"), QV4::Value::fromInt32(Exception));
+
+ o->put(v4->newString("status"), QV4::Value::fromInt32(status));
+
+ return QV4::Value::fromObject(o);
+}
+
+void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status)
+{
+ QV4::FunctionObject *f = callback.asFunctionObject();
+ if (!f)
+ return;
+
+ QV4::Value args[] = { status };
+ QV4::ExecutionContext *ctx = f->engine()->current;
+ try {
+ f->call(QV4::Value::fromObject(f->engine()->globalObject), args, 1);
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ }
+}
+
+QV4::Value QV4Include::result()
+{
+ return m_resultObject.value();
+}
+
+#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15
+void QV4Include::finished()
+{
+ m_redirectCount++;
+
+ if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) {
+ QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirect.isValid()) {
+ m_url = m_url.resolved(redirect.toUrl());
+ delete m_reply;
+
+ QNetworkRequest request;
+ request.setUrl(m_url);
+
+ m_reply = m_network->get(request);
+ QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
+ return;
+ }
+ }
+
+ if (m_reply->error() == QNetworkReply::NoError) {
+ QByteArray data = m_reply->readAll();
+
+ QString code = QString::fromUtf8(data);
+ QQmlScript::Parser::extractPragmas(code);
+
+ QV4::Script script(v4, m_qmlglobal.value().asObject(), code, m_url.toString());
+
+ QV4::ExecutionContext *ctx = v4->current;
+ QV4::Object *o = m_resultObject.value().asObject();
+ try {
+ script.parse();
+ script.run();
+ o->put(v4->newString("status"), QV4::Value::fromInt32(Ok));
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ o->put(v4->newString("status"), QV4::Value::fromInt32(Exception));
+ o->put(v4->newString("exception"), e.value());
+ }
+ } else {
+ m_resultObject.value().asObject()->put(v4->newString("status"), QV4::Value::fromInt32(NetworkError));
+ }
+
+ callback(m_callbackFunction.value(), m_resultObject.value());
+
+ disconnect();
+ deleteLater();
+}
+
+/*
+ Documented in qv8engine.cpp
+*/
+QV4::Value QV4Include::include(QV4::SimpleCallContext *ctx)
+{
+ if (!ctx->argumentCount)
+ return QV4::Value::undefinedValue();
+
+ QV4::ExecutionEngine *v4 = ctx->engine;
+ QV8Engine *engine = v4->v8Engine;
+ QQmlContextData *context = QV4::QmlContextWrapper::callingContext(v4);
+
+ if (!context || !context->isJSContext)
+ V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files");
+
+ QUrl url(ctx->engine->resolvedUrl(ctx->arguments[0].toQString()));
+
+ QV4::Value callbackFunction = QV4::Value::undefinedValue();
+ if (ctx->argumentCount >= 2 && ctx->arguments[1].asFunctionObject())
+ callbackFunction = ctx->arguments[1];
+
+ QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
+
+ QV4::Value result = QV4::Value::undefinedValue();
+
+ if (localFile.isEmpty()) {
+
+ QV4Include *i = new QV4Include(url, engine, context,
+ QV4::Value::fromObject(v4->qmlContextObject()),
+ callbackFunction);
+ result = i->result();
+
+ } else {
+
+ QFile f(localFile);
+
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data = f.readAll();
+ QString code = QString::fromUtf8(data);
+ QQmlScript::Parser::extractPragmas(code);
+
+ QV4::Object *qmlglobal = v4->qmlContextObject();
+ QV4::Script script(v4, qmlglobal, code, url.toString());
+
+ QV4::ExecutionContext *ctx = v4->current;
+ try {
+ script.parse();
+ script.run();
+ result = resultValue(v4, Ok);
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ result = resultValue(v4, Exception);
+ result.asObject()->put(v4->newString("exception"), e.value());
+ }
+ } else {
+ result = resultValue(v4, NetworkError);
+ }
+
+ callback(callbackFunction, result);
+ }
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4include_p.h
index fb23e863af..d6bbcd1a60 100644
--- a/src/qml/qml/v4/qv4program_p.h
+++ b/src/qml/qml/v4/qv4include_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QV4PROGRAM_P_H
-#define QV4PROGRAM_P_H
+#ifndef QV4INCLUDE_P_H
+#define QV4INCLUDE_P_H
//
// W A R N I N G
@@ -53,76 +53,61 @@
// We mean it.
//
-#include "qv4instruction_p.h"
+#include <QtCore/qobject.h>
+#include <QtCore/qurl.h>
-#ifdef Q_CC_MSVC
-// nonstandard extension used : zero-sized array in struct/union.
-# pragma warning( disable : 4200 )
-#endif
+#include <private/qqmlcontext_p.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4context_p.h>
QT_BEGIN_NAMESPACE
-struct QV4Program {
- quint32 bindings;
- quint32 dataLength;
- quint32 signalTableOffset;
- quint32 exceptionDataOffset;
- quint16 subscriptions;
- quint16 instructionCount;
-
- struct BindingReference {
- quint32 binding;
- quint32 blockMask;
- };
-
- struct BindingReferenceList {
- quint32 count;
- BindingReference bindings[];
+class QQmlEngine;
+class QNetworkAccessManager;
+class QNetworkReply;
+class QV8Engine;
+class QV4Include : public QObject
+{
+ Q_OBJECT
+public:
+ enum Status {
+ Ok = 0,
+ Loading = 1,
+ NetworkError = 2,
+ Exception = 3
};
- inline const char *data() const;
- inline const char *instructions() const;
- inline BindingReferenceList *signalTable(int signalIndex) const;
-};
+ static QV4::Value include(QV4::SimpleCallContext *ctx);
-enum QQmlRegisterType {
- UndefinedType,
- NullType,
- QObjectStarType,
- NumberType,
- FloatType,
- IntType,
- BoolType,
- SpecialNumericType,
-
- PODValueType,
-
- FirstCleanupType,
- QStringType = FirstCleanupType,
- QUrlType,
- QVariantType,
- QColorType,
- V8HandleType,
- QJSValueType
-};
+private slots:
+ void finished();
+
+private:
+ QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context,
+ const QV4::Value &qmlglobal, const QV4::Value &callback);
+ ~QV4Include();
-const char *QV4Program::data() const
-{
- return ((const char *)this) + sizeof(QV4Program);
-}
+ QV4::Value result();
-const char *QV4Program::instructions() const
-{
- return (const char *)(data() + dataLength);
-}
+ static QV4::Value resultValue(QV4::ExecutionEngine *v4, Status status = Loading);
+ static void callback(const QV4::Value &callback, const QV4::Value &status);
-QV4Program::BindingReferenceList *QV4Program::signalTable(int signalIndex) const
-{
- quint32 *signalTable = (quint32 *)(data() + signalTableOffset);
- return (BindingReferenceList *)(signalTable + signalTable[signalIndex]);
-}
+ QV4::ExecutionEngine *v4;
+ QNetworkAccessManager *m_network;
+ QPointer<QNetworkReply> m_reply;
+
+ QUrl m_url;
+ int m_redirectCount;
+
+ QV4::PersistentValue m_callbackFunction;
+ QV4::PersistentValue m_resultObject;
+
+ QQmlGuardedContextData m_context;
+ QV4::PersistentValue m_qmlglobal;
+};
QT_END_NAMESPACE
-#endif // QV4PROGRAM_P_H
+#endif
diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp
deleted file mode 100644
index 103a43a3a3..0000000000
--- a/src/qml/qml/v4/qv4instruction.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4instruction_p.h"
-#include "qv4bindings_p.h"
-
-#include <QtCore/qdebug.h>
-#include <private/qqmlglobal_p.h>
-
-// Define this to do a test dump of all the instructions at startup. This is
-// helpful to test that each instruction's Instr::dump() case uses the correct
-// number of tabs etc and otherwise looks correct.
-// #define DEBUG_INSTR_DUMP
-
-QT_BEGIN_NAMESPACE
-
-namespace QQmlJS {
-
-#ifdef DEBUG_INSTR_DUMP
-static struct DumpInstrAtStartup {
- DumpInstrAtStartup() {
- Bytecode bc;
-#define DUMP_INSTR_AT_STARTUP(I, FMT) { V4InstrData<V4Instr::I> i; bc.append(i); }
- FOR_EACH_V4_INSTR(DUMP_INSTR_AT_STARTUP);
-#undef DUMP_INSTR_AT_STARTUP
- const char *start = bc.constData();
- const char *end = start + bc.size();
- bc.dump(start, end);
- }
-} dump_instr_at_startup;
-#endif
-
-int V4Instr::size(Type type)
-{
-#define V4_RETURN_INSTR_SIZE(I, FMT) case I: return QML_V4_INSTR_SIZE(I, FMT);
- switch (type) {
- FOR_EACH_V4_INSTR(V4_RETURN_INSTR_SIZE)
- }
-#undef V4_RETURN_INSTR_SIZE
- return 0;
-}
-
-void Bytecode::dump(const V4Instr *i, int address) const
-{
- QByteArray leading;
- if (address != -1) {
- leading = QByteArray::number(address);
- leading.prepend(QByteArray(8 - leading.count(), ' '));
- leading.append('\t');
- }
-
-#define INSTR_DUMP qWarning().nospace() << leading.constData()
-
- switch (instructionType(i)) {
- case V4Instr::Noop:
- INSTR_DUMP << '\t' << "Noop";
- break;
- case V4Instr::BindingId:
- INSTR_DUMP << i->id.line << ':' << i->id.column << ':';
- break;
- case V4Instr::SubscribeId:
- INSTR_DUMP << '\t' << "SubscribeId" << "\t\t" << "Id_Offset(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ')';
- break;
- case V4Instr::FetchAndSubscribe:
- INSTR_DUMP << '\t' << "FetchAndSubscribe" << '\t' << "Object_Reg(" << i->fetchAndSubscribe.reg << ") Fast_Accessor(" << i->fetchAndSubscribe.property.accessors << ") -> Output_Reg(" << i->fetchAndSubscribe.reg << ") Subscription_Slot(" << i->fetchAndSubscribe.subscription << ')';
- break;
- case V4Instr::LoadId:
- INSTR_DUMP << '\t' << "LoadId" << "\t\t\t" << "Id_Offset(" << i->load.index << ") -> Output_Reg(" << i->load.reg << ')';
- break;
- case V4Instr::LoadScope:
- INSTR_DUMP << '\t' << "LoadScope" << "\t\t" << "-> Output_Reg(" << i->load.reg << ')';
- break;
- case V4Instr::LoadRoot:
- INSTR_DUMP << '\t' << "LoadRoot" << "\t\t" << "-> Output_Reg(" << i->load.reg << ')';
- break;
- case V4Instr::LoadSingletonObject:
- INSTR_DUMP << '\t' << "LoadSingletonObject" << "\t\t" << ") -> Output_Reg(" << i->load.reg << ')';
- break;
- case V4Instr::LoadAttached:
- INSTR_DUMP << '\t' << "LoadAttached" << "\t\t" << "Object_Reg(" << i->attached.reg << ") Attached_Index(" << i->attached.id << ") -> Output_Reg(" << i->attached.output << ')';
- break;
- case V4Instr::UnaryNot:
- INSTR_DUMP << '\t' << "UnaryNot" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::UnaryMinusNumber:
- INSTR_DUMP << '\t' << "UnaryMinusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::UnaryMinusInt:
- INSTR_DUMP << '\t' << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::UnaryPlusNumber:
- INSTR_DUMP << '\t' << "UnaryPlusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::UnaryPlusInt:
- INSTR_DUMP << '\t' << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToInt:
- INSTR_DUMP << '\t' << "ConvertBoolToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToJSValue:
- INSTR_DUMP << '\t' << "ConvertBoolToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToNumber:
- INSTR_DUMP << '\t' << "ConvertBoolToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToString:
- INSTR_DUMP << '\t' << "ConvertBoolToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToVariant:
- INSTR_DUMP << '\t' << "ConvertBoolToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertBoolToVar:
- INSTR_DUMP << '\t' << "ConvertBoolToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToBool:
- INSTR_DUMP << '\t' << "ConvertIntToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToJSValue:
- INSTR_DUMP << '\t' << "ConvertIntToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToNumber:
- INSTR_DUMP << '\t' << "ConvertIntToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToString:
- INSTR_DUMP << '\t' << "ConvertIntToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToVariant:
- INSTR_DUMP << '\t' << "ConvertIntToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertIntToVar:
- INSTR_DUMP << '\t' << "ConvertIntToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertJSValueToVar:
- INSTR_DUMP << '\t' << "ConvertJSValueToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToBool:
- INSTR_DUMP << '\t' << "ConvertNumberToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToInt:
- INSTR_DUMP << '\t' << "ConvertNumberToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToJSValue:
- INSTR_DUMP << '\t' << "ConvertNumberToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToString:
- INSTR_DUMP << '\t' << "ConvertNumberToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToVariant:
- INSTR_DUMP << '\t' << "ConvertNumberToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNumberToVar:
- INSTR_DUMP << '\t' << "ConvertNumberToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToBool:
- INSTR_DUMP << '\t' << "ConvertStringToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToInt:
- INSTR_DUMP << '\t' << "ConvertStringToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToJSValue:
- INSTR_DUMP << '\t' << "ConvertStringToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToNumber:
- INSTR_DUMP << '\t' << "ConvertStringToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToUrl:
- INSTR_DUMP << '\t' << "ConvertStringToUrl" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToColor:
- INSTR_DUMP << '\t' << "ConvertStringToColor" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToVariant:
- INSTR_DUMP << '\t' << "ConvertStringToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertStringToVar:
- INSTR_DUMP << '\t' << "ConvertStringToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertUrlToBool:
- INSTR_DUMP << '\t' << "ConvertUrlToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertUrlToJSValue:
- INSTR_DUMP << '\t' << "ConvertUrlToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertUrlToString:
- INSTR_DUMP << '\t' << "ConvertUrlToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertUrlToVariant:
- INSTR_DUMP << '\t' << "ConvertUrlToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertUrlToVar:
- INSTR_DUMP << '\t' << "ConvertUrlToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertColorToBool:
- INSTR_DUMP << '\t' << "ConvertColorToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertColorToJSValue:
- INSTR_DUMP << '\t' << "ConvertColorToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertColorToString:
- INSTR_DUMP << '\t' << "ConvertColorToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertColorToVariant:
- INSTR_DUMP << '\t' << "ConvertColorToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertColorToVar:
- INSTR_DUMP << '\t' << "ConvertColorToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertObjectToBool:
- INSTR_DUMP << '\t' << "ConvertObjectToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertObjectToJSValue:
- INSTR_DUMP << '\t' << "ConvertObjectToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertObjectToVariant:
- INSTR_DUMP << '\t' << "ConvertObjectToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertObjectToVar:
- INSTR_DUMP << '\t' << "ConvertObjectToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertVarToJSValue:
- INSTR_DUMP << '\t' << "ConvertVarToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNullToJSValue:
- INSTR_DUMP << '\t' << "ConvertNullToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNullToObject:
- INSTR_DUMP << '\t' << "ConvertNullToObject" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNullToVariant:
- INSTR_DUMP << '\t' << "ConvertNullToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ConvertNullToVar:
- INSTR_DUMP << '\t' << "ConvertNullToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::ResolveUrl:
- INSTR_DUMP << '\t' << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathSinNumber:
- INSTR_DUMP << '\t' << "MathSinNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathCosNumber:
- INSTR_DUMP << '\t' << "MathCosNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathAbsNumber:
- INSTR_DUMP << '\t' << "MathAbsNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathRoundNumber:
- INSTR_DUMP << '\t' << "MathRoundNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathFloorNumber:
- INSTR_DUMP << '\t' << "MathFloorNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathCeilNumber:
- INSTR_DUMP << '\t' << "MathCeilNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::MathPINumber:
- INSTR_DUMP << '\t' << "MathPINumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')';
- break;
- case V4Instr::LoadNull:
- INSTR_DUMP << '\t' << "LoadNull" << "\t\t" << "Constant(null) -> Output_Reg(" << i->null_value.reg << ')';
- break;
- case V4Instr::LoadNumber:
- INSTR_DUMP << '\t' << "LoadNumber" << "\t\t" << "Constant(" << i->number_value.value << ") -> Output_Reg(" << i->number_value.reg << ')';
- break;
- case V4Instr::LoadInt:
- INSTR_DUMP << '\t' << "LoadInt" << "\t\t\t" << "Constant(" << i->int_value.value << ") -> Output_Reg(" << i->int_value.reg << ')';
- break;
- case V4Instr::LoadBool:
- INSTR_DUMP << '\t' << "LoadBool" << "\t\t" << "Constant(" << i->bool_value.value << ") -> Output_Reg(" << i->bool_value.reg << ')';
- break;
- case V4Instr::LoadString:
- INSTR_DUMP << '\t' << "LoadString" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ") -> Output_Register(" << i->string_value.reg << ')';
- break;
- case V4Instr::EnableV4Test:
- INSTR_DUMP << '\t' << "EnableV4Test" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ')';
- break;
- case V4Instr::TestV4Store:
- INSTR_DUMP << '\t' << "TestV4Store" << "\t\t" << "Input_Reg(" << i->storetest.reg << ") Reg_Type(" << i->storetest.regType << ')';
- break;
- case V4Instr::BitAndInt:
- INSTR_DUMP << '\t' << "BitAndInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::BitOrInt:
- INSTR_DUMP << '\t' << "BitOrInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::BitXorInt:
- INSTR_DUMP << '\t' << "BitXorInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::AddNumber:
- INSTR_DUMP << '\t' << "AddNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::AddString:
- INSTR_DUMP << '\t' << "AddString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::SubNumber:
- INSTR_DUMP << '\t' << "SubNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::MulNumber:
- INSTR_DUMP << '\t' << "MulNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::DivNumber:
- INSTR_DUMP << '\t' << "DivNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::ModNumber:
- INSTR_DUMP << '\t' << "ModNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::LShiftInt:
- INSTR_DUMP << '\t' << "LShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::RShiftInt:
- INSTR_DUMP << '\t' << "RShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::URShiftInt:
- INSTR_DUMP << '\t' << "URShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::GtNumber:
- INSTR_DUMP << '\t' << "GtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::LtNumber:
- INSTR_DUMP << '\t' << "LtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::GeNumber:
- INSTR_DUMP << '\t' << "GeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::LeNumber:
- INSTR_DUMP << '\t' << "LeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::EqualNumber:
- INSTR_DUMP << '\t' << "EqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::NotEqualNumber:
- INSTR_DUMP << '\t' << "NotEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictEqualNumber:
- INSTR_DUMP << '\t' << "StrictEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictNotEqualNumber:
- INSTR_DUMP << '\t' << "StrictNotEqualNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::GtString:
- INSTR_DUMP << '\t' << "GtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::LtString:
- INSTR_DUMP << '\t' << "LtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::GeString:
- INSTR_DUMP << '\t' << "GeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::LeString:
- INSTR_DUMP << '\t' << "LeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::EqualString:
- INSTR_DUMP << '\t' << "EqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::NotEqualString:
- INSTR_DUMP << '\t' << "NotEqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictEqualString:
- INSTR_DUMP << '\t' << "StrictEqualString" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictNotEqualString:
- INSTR_DUMP << '\t' << "StrictNotEqualString" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::EqualObject:
- INSTR_DUMP << '\t' << "EqualObject" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::NotEqualObject:
- INSTR_DUMP << '\t' << "NotEqualObject" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictEqualObject:
- INSTR_DUMP << '\t' << "StrictEqualObject" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::StrictNotEqualObject:
- INSTR_DUMP << '\t' << "StrictNotEqualObject" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::MathMaxNumber:
- INSTR_DUMP << '\t' << "MathMaxNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::MathMinNumber:
- INSTR_DUMP << '\t' << "MathMinNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')';
- break;
- case V4Instr::NewString:
- INSTR_DUMP << '\t' << "NewString" << "\t\t" << "Register(" << i->construct.reg << ')';
- break;
- case V4Instr::NewUrl:
- INSTR_DUMP << '\t' << "NewUrl" << "\t\t\t" << "Register(" << i->construct.reg << ')';
- break;
- case V4Instr::CleanupRegister:
- INSTR_DUMP << '\t' << "CleanupRegister" << "\t\t" << "Register(" << i->cleanup.reg << ')';
- break;
- case V4Instr::Fetch:
- INSTR_DUMP << '\t' << "Fetch" << "\t\t\t" << "Object_Reg(" << i->fetch.reg << ") Property_Index(" << i->fetch.index << ") -> Output_Reg(" << i->fetch.reg << ')';
- break;
- case V4Instr::Store:
- INSTR_DUMP << '\t' << "Store" << "\t\t\t" << "Input_Reg(" << i->store.reg << ") -> Object_Reg(" << i->store.output << ") Property_Index(" << i->store.index << ')';
- break;
- case V4Instr::Copy:
- INSTR_DUMP << '\t' << "Copy" << "\t\t\t" << "Input_Reg(" << i->copy.src << ") -> Output_Reg(" << i->copy.reg << ')';
- break;
- case V4Instr::Jump:
- if (i->jump.reg != -1) {
- INSTR_DUMP << '\t' << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ") [if false == Input_Reg(" << i->jump.reg << ")]";
- } else {
- INSTR_DUMP << '\t' << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ')';
- }
- break;
- case V4Instr::BranchFalse:
- INSTR_DUMP << '\t' << "BranchFalse" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if false == Input_Reg(" << i->branchop.reg << ")]";
- break;
- case V4Instr::BranchTrue:
- INSTR_DUMP << '\t' << "BranchTrue" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if true == Input_Reg(" << i->branchop.reg << ")]";
- break;
- case V4Instr::Branch:
- INSTR_DUMP << '\t' << "Branch" << "\t\t\t" << "Address(" << (address + size() + i->branchop.offset) << ')';
- break;
- case V4Instr::Block:
- INSTR_DUMP << '\t' << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(i->blockop.block, 16).constData() << ')';
- break;
- case V4Instr::Throw:
- INSTR_DUMP << '\t' << "Throw" << "\t\t\t" << "InputReg(" << i->throwop.message << ')';
- break;
- default:
- INSTR_DUMP << '\t' << "Unknown";
- break;
- }
-}
-
-void Bytecode::dump(const char *start, const char *end) const
-{
- const char *code = start;
- while (code < end) {
- const V4Instr *instr = reinterpret_cast<const V4Instr *>(code);
- dump(instr, code - start);
- code += V4Instr::size(instructionType(instr));
- }
-}
-
-Bytecode::Bytecode()
-{
-#ifdef QML_THREADED_INTERPRETER
- decodeInstr = QV4Bindings::getDecodeInstrTable();
-#endif
-}
-
-V4Instr::Type Bytecode::instructionType(const V4Instr *instr) const
-{
-#ifdef QML_THREADED_INTERPRETER
- void *code = instr->common.code;
-
-# define CHECK_V4_INSTR_CODE(I, FMT) \
- if (decodeInstr[static_cast<int>(V4Instr::I)] == code) \
- return V4Instr::I;
-
- FOR_EACH_V4_INSTR(CHECK_V4_INSTR_CODE)
- Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid instruction address");
- return static_cast<V4Instr::Type>(0);
-# undef CHECK_V4_INSTR_CODE
-#else
- return static_cast<V4Instr::Type>(instr->common.type);
-#endif
-
-}
-
-void Bytecode::append(V4Instr::Type type, V4Instr &instr)
-{
-#ifdef QML_THREADED_INTERPRETER
- instr.common.code = decodeInstr[static_cast<int>(type)];
-#else
- instr.common.type = type;
-#endif
- d.append(reinterpret_cast<const char *>(&instr), V4Instr::size(type));
-}
-
-int Bytecode::remove(int offset)
-{
- const V4Instr *instr = reinterpret_cast<const V4Instr *>(d.begin() + offset);
- const int instrSize = V4Instr::size(instructionType(instr));
- d.remove(offset, instrSize);
- return instrSize;
-}
-
-const V4Instr &Bytecode::operator[](int offset) const
-{
- return *(reinterpret_cast<const V4Instr *>(d.begin() + offset));
-}
-
-V4Instr &Bytecode::operator[](int offset)
-{
- return *(reinterpret_cast<V4Instr *>(d.begin() + offset));
-}
-
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h
deleted file mode 100644
index 9797abf69d..0000000000
--- a/src/qml/qml/v4/qv4instruction_p.h
+++ /dev/null
@@ -1,483 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4INSTRUCTION_P_H
-#define QV4INSTRUCTION_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/qglobal.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qvector.h>
-#include <QtCore/qvarlengtharray.h>
-
-#include <private/qqmlpropertycache_p.h>
-
-QT_BEGIN_NAMESPACE
-
-#define FOR_EACH_V4_INSTR(F) \
- F(Noop, common) \
- F(BindingId, id) \
- F(SubscribeId, subscribeop) \
- F(FetchAndSubscribe, fetchAndSubscribe) \
- F(LoadId, load) \
- F(LoadScope, load) \
- F(LoadRoot, load) \
- F(LoadSingletonObject, load) \
- F(LoadAttached, attached) \
- F(UnaryNot, unaryop) \
- F(UnaryMinusNumber, unaryop) \
- F(UnaryMinusInt, unaryop) \
- F(UnaryPlusNumber, unaryop) \
- F(UnaryPlusInt, unaryop) \
- F(ConvertBoolToInt, unaryop) \
- F(ConvertBoolToJSValue, unaryop) \
- F(ConvertBoolToNumber, unaryop) \
- F(ConvertBoolToString, unaryop) \
- F(ConvertBoolToVariant, unaryop) \
- F(ConvertBoolToVar, unaryop) \
- F(ConvertIntToBool, unaryop) \
- F(ConvertIntToJSValue, unaryop) \
- F(ConvertIntToNumber, unaryop) \
- F(ConvertIntToString, unaryop) \
- F(ConvertIntToVariant, unaryop) \
- F(ConvertIntToVar, unaryop) \
- F(ConvertJSValueToVar, unaryop) \
- F(ConvertNumberToBool, unaryop) \
- F(ConvertNumberToInt, unaryop) \
- F(ConvertNumberToJSValue, unaryop) \
- F(ConvertNumberToString, unaryop) \
- F(ConvertNumberToVariant, unaryop) \
- F(ConvertNumberToVar, unaryop) \
- F(ConvertStringToBool, unaryop) \
- F(ConvertStringToInt, unaryop) \
- F(ConvertStringToJSValue, unaryop) \
- F(ConvertStringToNumber, unaryop) \
- F(ConvertStringToUrl, unaryop) \
- F(ConvertStringToColor, unaryop) \
- F(ConvertStringToVariant, unaryop) \
- F(ConvertStringToVar, unaryop) \
- F(ConvertUrlToBool, unaryop) \
- F(ConvertUrlToJSValue, unaryop) \
- F(ConvertUrlToString, unaryop) \
- F(ConvertUrlToVariant, unaryop) \
- F(ConvertUrlToVar, unaryop) \
- F(ConvertColorToBool, unaryop) \
- F(ConvertColorToJSValue, unaryop) \
- F(ConvertColorToString, unaryop) \
- F(ConvertColorToVariant, unaryop) \
- F(ConvertColorToVar, unaryop) \
- F(ConvertObjectToBool, unaryop) \
- F(ConvertObjectToJSValue, unaryop) \
- F(ConvertObjectToVariant, unaryop) \
- F(ConvertObjectToVar, unaryop) \
- F(ConvertVarToJSValue, unaryop) \
- F(ConvertNullToJSValue, unaryop) \
- F(ConvertNullToObject, unaryop) \
- F(ConvertNullToVariant, unaryop) \
- F(ConvertNullToVar, unaryop) \
- F(ResolveUrl, unaryop) \
- F(MathSinNumber, unaryop) \
- F(MathCosNumber, unaryop) \
- F(MathAbsNumber, unaryop) \
- F(MathRoundNumber, unaryop) \
- F(MathFloorNumber, unaryop) \
- F(MathCeilNumber, unaryop) \
- F(MathPINumber, unaryop) \
- F(LoadNull, null_value) \
- F(LoadNumber, number_value) \
- F(LoadInt, int_value) \
- F(LoadBool, bool_value) \
- F(LoadString, string_value) \
- F(EnableV4Test, string_value) \
- F(TestV4Store, storetest) \
- F(BitAndInt, binaryop) \
- F(BitOrInt, binaryop) \
- F(BitXorInt, binaryop) \
- F(AddNumber, binaryop) \
- F(AddString, binaryop) \
- F(SubNumber, binaryop) \
- F(MulNumber, binaryop) \
- F(DivNumber, binaryop) \
- F(ModNumber, binaryop) \
- F(LShiftInt, binaryop) \
- F(RShiftInt, binaryop) \
- F(URShiftInt, binaryop) \
- F(GtNumber, binaryop) \
- F(LtNumber, binaryop) \
- F(GeNumber, binaryop) \
- F(LeNumber, binaryop) \
- F(EqualNumber, binaryop) \
- F(NotEqualNumber, binaryop) \
- F(StrictEqualNumber, binaryop) \
- F(StrictNotEqualNumber, binaryop) \
- F(GtString, binaryop) \
- F(LtString, binaryop) \
- F(GeString, binaryop) \
- F(LeString, binaryop) \
- F(EqualString, binaryop) \
- F(NotEqualString, binaryop) \
- F(StrictEqualString, binaryop) \
- F(StrictNotEqualString, binaryop) \
- F(EqualObject, binaryop) \
- F(NotEqualObject, binaryop) \
- F(StrictEqualObject, binaryop) \
- F(StrictNotEqualObject, binaryop) \
- F(MathMaxNumber, binaryop) \
- F(MathMinNumber, binaryop) \
- F(NewString, construct) \
- F(NewUrl, construct) \
- F(CleanupRegister, cleanup) \
- F(Copy, copy) \
- F(Fetch, fetch) \
- F(Store, store) \
- F(Jump, jump) \
- F(BranchTrue, branchop) \
- F(BranchFalse, branchop) \
- F(Branch, branchop) \
- F(Block, blockop) \
- F(Throw, throwop)
-
-#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200)
-# define QML_THREADED_INTERPRETER
-#endif
-
-#ifdef Q_ALIGNOF
-# define QML_V4_INSTR_ALIGN_MASK (Q_ALIGNOF(V4Instr) - 1)
-#else
-# define QML_V4_INSTR_ALIGN_MASK (sizeof(void *) - 1)
-#endif
-
-#define QML_V4_INSTR_ENUM(I, FMT) I,
-#define QML_V4_INSTR_ADDR(I, FMT) &&op_##I,
-#define QML_V4_INSTR_SIZE(I, FMT) ((sizeof(V4Instr::instr_##FMT) + QML_V4_INSTR_ALIGN_MASK) & ~QML_V4_INSTR_ALIGN_MASK)
-
-#ifdef QML_THREADED_INTERPRETER
-# define QML_V4_BEGIN_INSTR(I,FMT) op_##I:
-# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; goto *instr->common.code;
-# define QML_V4_INSTR_HEADER void *code;
-#else
-# define QML_V4_BEGIN_INSTR(I,FMT) case V4Instr::I:
-# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; break;
-# define QML_V4_INSTR_HEADER quint8 type;
-#endif
-
-class QObject;
-class QQmlNotifier;
-
-namespace QQmlJS {
-
-union Q_AUTOTEST_EXPORT V4Instr {
- enum Type {
- FOR_EACH_V4_INSTR(QML_V4_INSTR_ENUM)
- };
-
- static int size(Type type);
-
- struct instr_common {
- QML_V4_INSTR_HEADER
- };
-
- struct instr_id {
- QML_V4_INSTR_HEADER
- quint16 column;
- quint16 line;
- };
-
- struct instr_init {
- QML_V4_INSTR_HEADER
- quint16 subscriptions;
- quint16 identifiers;
- };
-
- struct instr_subscribeop {
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint16 offset;
- quint32 index;
- };
-
- struct instr_load {
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint32 index;
- };
-
- struct instr_attached {
- QML_V4_INSTR_HEADER
- qint8 output;
- qint8 reg;
- quint8 exceptionId;
- quint32 id;
- };
-
- struct instr_store {
- QML_V4_INSTR_HEADER
- qint8 output;
- qint8 reg;
- quint8 exceptionId;
- quint8 valueType;
- quint32 index;
- };
-
- struct instr_storetest {
- QML_V4_INSTR_HEADER
- qint8 reg;
- qint32 regType;
- };
-
- struct instr_fetchAndSubscribe {
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint8 exceptionId;
- quint8 valueType;
- quint16 subscription;
- QQmlPropertyRawData property;
- };
-
- struct instr_fetch{
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint8 exceptionId;
- quint8 valueType;
- quint32 index;
- quint16 subOffset;
- quint32 subIndex;
- };
-
- struct instr_copy {
- QML_V4_INSTR_HEADER
- qint8 reg;
- qint8 src;
- };
-
- struct instr_construct {
- QML_V4_INSTR_HEADER
- qint8 reg;
- };
-
- struct instr_null_value {
- QML_V4_INSTR_HEADER
- qint8 reg;
- };
-
- struct instr_number_value {
- QML_V4_INSTR_HEADER
- qint8 reg;
- double value; // XXX Makes the instruction 12 bytes
- };
-
- struct instr_int_value {
- QML_V4_INSTR_HEADER
- qint8 reg;
- int value;
- };
-
- struct instr_bool_value {
- QML_V4_INSTR_HEADER
- qint8 reg;
- bool value;
- };
-
- struct instr_string_value {
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint16 length;
- quint32 offset;
- };
-
- struct instr_binaryop {
- QML_V4_INSTR_HEADER
- qint8 output;
- qint8 left;
- qint8 right;
- };
-
- struct instr_unaryop {
- QML_V4_INSTR_HEADER
- qint8 output;
- qint8 src;
- };
-
- struct instr_jump {
- QML_V4_INSTR_HEADER
- qint8 reg;
- quint32 count;
- };
-
- struct instr_find {
- QML_V4_INSTR_HEADER
- qint8 reg;
- qint8 src;
- quint8 exceptionId;
- quint16 name;
- quint16 subscribeIndex;
- };
-
- struct instr_cleanup {
- QML_V4_INSTR_HEADER
- qint8 reg;
- };
-
- struct instr_initstring {
- QML_V4_INSTR_HEADER
- quint16 offset;
- quint32 dataIdx;
- };
-
- struct instr_branchop {
- QML_V4_INSTR_HEADER
- quint8 reg;
- qint16 offset;
- };
-
- struct instr_blockop {
- QML_V4_INSTR_HEADER
- quint32 block;
- };
-
- struct instr_throwop {
- QML_V4_INSTR_HEADER
- quint8 exceptionId;
- quint32 message;
- };
-
- instr_common common;
- instr_id id;
- instr_init init;
- instr_subscribeop subscribeop;
- instr_load load;
- instr_attached attached;
- instr_store store;
- instr_storetest storetest;
- instr_fetchAndSubscribe fetchAndSubscribe;
- instr_fetch fetch;
- instr_copy copy;
- instr_construct construct;
- instr_null_value null_value;
- instr_number_value number_value;
- instr_int_value int_value;
- instr_bool_value bool_value;
- instr_string_value string_value;
- instr_binaryop binaryop;
- instr_unaryop unaryop;
- instr_jump jump;
- instr_find find;
- instr_cleanup cleanup;
- instr_initstring initstring;
- instr_branchop branchop;
- instr_blockop blockop;
- instr_throwop throwop;
-};
-
-template<int N>
-struct V4InstrMeta {
-};
-
-#define QML_V4_INSTR_META_TEMPLATE(I, FMT) \
- template<> struct V4InstrMeta<(int)V4Instr::I> { \
- enum { Size = QML_V4_INSTR_SIZE(I, FMT) }; \
- typedef V4Instr::instr_##FMT DataType; \
- static const DataType &data(const V4Instr &instr) { return instr.FMT; } \
- static void setData(V4Instr &instr, const DataType &v) { instr.FMT = v; } \
- };
-FOR_EACH_V4_INSTR(QML_V4_INSTR_META_TEMPLATE);
-#undef QML_V4_INSTR_META_TEMPLATE
-
-template<int Instr>
-class Q_AUTOTEST_EXPORT V4InstrData : public V4InstrMeta<Instr>::DataType
-{
-};
-
-class Q_AUTOTEST_EXPORT Bytecode
-{
- Q_DISABLE_COPY(Bytecode)
-
-public:
- Bytecode();
-
- const char *constData() const { return d.constData(); }
- int size() const { return d.size(); }
- int count() const { return d.count(); }
- void clear() { d.clear(); }
- bool isEmpty() const { return d.isEmpty(); }
- V4Instr::Type instructionType(const V4Instr *instr) const;
-
- template <int Instr>
- void append(const V4InstrData<Instr> &data)
- {
- V4Instr genericInstr;
- V4InstrMeta<Instr>::setData(genericInstr, data);
- return append(static_cast<V4Instr::Type>(Instr), genericInstr);
- }
- void append(V4Instr::Type type, V4Instr &instr);
-
- int remove(int index);
-
- const V4Instr &operator[](int offset) const;
- V4Instr &operator[](int offset);
-
- void dump(const char *start, const char *end) const;
-
-private:
- void dump(const V4Instr *instr, int = -1) const;
-
- QVarLengthArray<char, 4 * 1024> d;
-#ifdef QML_THREADED_INTERPRETER
- void **decodeInstr;
-#endif
-};
-
-}
-
-QT_END_NAMESPACE
-
-#endif // QV4INSTRUCTION_P_H
-
diff --git a/src/qml/qml/v4/qv4internalclass.cpp b/src/qml/qml/v4/qv4internalclass.cpp
new file mode 100644
index 0000000000..f4edc99545
--- /dev/null
+++ b/src/qml/qml/v4/qv4internalclass.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4internalclass_p.h>
+#include <qv4string_p.h>
+#include <qv4engine_p.h>
+#include <qv4identifier_p.h>
+#include "qv4object_p.h"
+#include "qv4identifiertable_p.h"
+
+QT_BEGIN_NAMESPACE
+
+uint QV4::qHash(const QV4::InternalClassTransition &t, uint)
+{
+ return t.id->hashValue ^ t.flags;
+}
+
+using namespace QV4;
+
+static const uchar prime_deltas[] = {
+ 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
+ 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
+};
+
+static inline int primeForNumBits(int numBits)
+{
+ return (1 << numBits) + prime_deltas[numBits];
+}
+
+PropertyHashData::PropertyHashData(int numBits)
+ : numBits(numBits)
+ , size(0)
+{
+ refCount.store(1);
+ alloc = primeForNumBits(numBits);
+ entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry));
+ memset(entries, 0, alloc*sizeof(PropertyHash::Entry));
+}
+
+void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
+{
+ // fill up to max 50%
+ bool grow = (d->alloc <= d->size*2);
+
+ if (classSize < d->size || grow) {
+ PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
+ for (uint i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier || e.index >= classSize)
+ continue;
+ uint idx = e.identifier->hashValue % dd->alloc;
+ while (dd->entries[idx].identifier) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ assert(d->refCount.load() > 1);
+ d->refCount.deref();
+ d = dd;
+ }
+
+ uint idx = entry.identifier->hashValue % d->alloc;
+ while (d->entries[idx].identifier) {
+ ++idx;
+ idx %= d->alloc;
+ }
+ d->entries[idx] = entry;
+ ++d->size;
+}
+
+uint PropertyHash::lookup(const Identifier *identifier) const
+{
+ assert(d->entries);
+
+ uint idx = identifier->hashValue % d->alloc;
+ while (1) {
+ if (d->entries[idx].identifier == identifier)
+ return d->entries[idx].index;
+ if (!d->entries[idx].identifier)
+ return UINT_MAX;
+ ++idx;
+ idx %= d->alloc;
+ }
+}
+
+
+InternalClass::InternalClass(const QV4::InternalClass &other)
+ : engine(other.engine)
+ , propertyTable(other.propertyTable)
+ , nameMap(other.nameMap)
+ , propertyData(other.propertyData)
+ , transitions()
+ , m_sealed(0)
+ , m_frozen(0)
+ , size(other.size)
+{
+}
+
+// ### Should we build this up from the empty class to avoid duplication?
+InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index)
+{
+// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all;
+ data.resolve();
+ uint idx = find(string);
+ if (index)
+ *index = idx;
+
+ assert(idx != UINT_MAX);
+
+ if (data == propertyData[idx])
+ return this;
+
+
+ Transition t = { string->identifier, (int)data.flags() };
+ QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t);
+ if (tit != transitions.constEnd())
+ return tit.value();
+
+ // create a new class and add it to the tree
+ InternalClass *newClass = engine->newClass(*this);
+ newClass->propertyData[idx] = data;
+ return newClass;
+
+}
+
+InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index)
+{
+// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type();
+ data.resolve();
+ engine->identifierTable->identifier(string);
+
+ if (propertyTable.lookup(string->identifier) < size)
+ return changeMember(string, data, index);
+
+ Transition t = { string->identifier, (int)data.flags() };
+ QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t);
+
+ if (index)
+ *index = size;
+ if (tit != transitions.constEnd())
+ return tit.value();
+
+ // create a new class and add it to the tree
+ InternalClass *newClass = engine->newClass(*this);
+ PropertyHash::Entry e = { string->identifier, size };
+ newClass->propertyTable.addEntry(e, size);
+
+ // The incoming string can come from anywhere, so make sure to
+ // store a string in the nameMap that's guaranteed to get
+ // marked properly during GC.
+ String *name = engine->newIdentifier(string->toQString());
+ newClass->nameMap.append(name);
+
+ newClass->propertyData.append(data);
+ ++newClass->size;
+ transitions.insert(t, newClass);
+ return newClass;
+}
+
+void InternalClass::removeMember(Object *object, Identifier *id)
+{
+ int propIdx = propertyTable.lookup(id);
+ assert(propIdx < size);
+
+ Transition t = { id, -1 };
+ QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t);
+
+ if (tit != transitions.constEnd()) {
+ object->internalClass = tit.value();
+ return;
+ }
+
+ // create a new class and add it to the tree
+ object->internalClass = engine->emptyClass;
+ for (int i = 0; i < nameMap.size(); ++i) {
+ if (i == propIdx)
+ continue;
+ object->internalClass = object->internalClass->addMember(nameMap.at(i), propertyData.at(i));
+ }
+
+ transitions.insert(t, object->internalClass);
+}
+
+uint InternalClass::find(String *string)
+{
+ engine->identifierTable->identifier(string);
+ const Identifier *id = string->identifier;
+
+ uint index = propertyTable.lookup(id);
+ if (index < size)
+ return index;
+
+ return UINT_MAX;
+}
+
+InternalClass *InternalClass::sealed()
+{
+ if (m_sealed)
+ return m_sealed;
+
+ m_sealed = engine->emptyClass;
+ for (int i = 0; i < nameMap.size(); ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ attrs.setConfigurable(false);
+ m_sealed = m_sealed->addMember(nameMap.at(i), attrs);
+ }
+
+ m_sealed->m_sealed = m_sealed;
+ return m_sealed;
+}
+
+InternalClass *InternalClass::frozen()
+{
+ if (m_frozen)
+ return m_frozen;
+
+ m_frozen = engine->emptyClass;
+ for (int i = 0; i < nameMap.size(); ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ attrs.setWritable(false);
+ attrs.setConfigurable(false);
+ m_frozen = m_frozen->addMember(nameMap.at(i), attrs);
+ }
+
+ m_frozen->m_frozen = m_frozen;
+ m_frozen->m_sealed = m_frozen;
+ return m_frozen;
+}
+
+void InternalClass::destroy()
+{
+ if (!engine)
+ return;
+ engine = 0;
+
+ // Free the memory of the hashes/vectors by calling clear(), which
+ // re-assigns them to the shared null instance. Therefore Internalclass
+ // doesn't need a destructor to be called.
+ propertyTable.~PropertyHash();
+ nameMap.clear();
+ propertyData.clear();
+
+ if (m_sealed)
+ m_sealed->destroy();
+
+ if (m_frozen)
+ m_frozen->destroy();
+
+ for (QHash<Transition, InternalClass *>::ConstIterator it = transitions.begin(), end = transitions.end();
+ it != end; ++it)
+ it.value()->destroy();
+
+ transitions.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4internalclass_p.h b/src/qml/qml/v4/qv4internalclass_p.h
new file mode 100644
index 0000000000..fc6c5352b1
--- /dev/null
+++ b/src/qml/qml/v4/qv4internalclass_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4INTERNALCLASS_H
+#define QV4INTERNALCLASS_H
+
+#include <QHash>
+#include <QVector>
+#include "qv4global_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct String;
+struct ExecutionEngine;
+struct Object;
+struct Identifier;
+
+struct PropertyHashData;
+struct PropertyHash
+{
+ struct Entry {
+ const Identifier *identifier;
+ uint index;
+ };
+
+ PropertyHashData *d;
+
+ inline PropertyHash();
+ inline PropertyHash(const PropertyHash &other);
+ inline ~PropertyHash();
+
+ void addEntry(const Entry &entry, int classSize);
+ uint lookup(const Identifier *identifier) const;
+
+private:
+ PropertyHash &operator=(const PropertyHash &other);
+};
+
+struct PropertyHashData
+{
+ PropertyHashData(int numBits);
+ ~PropertyHashData() {
+ free(entries);
+ }
+
+ QBasicAtomicInt refCount;
+ int alloc;
+ int size;
+ int numBits;
+ PropertyHash::Entry *entries;
+};
+
+inline PropertyHash::PropertyHash()
+{
+ d = new PropertyHashData(3);
+}
+
+inline PropertyHash::PropertyHash(const PropertyHash &other)
+{
+ d = other.d;
+ d->refCount.ref();
+}
+
+inline PropertyHash::~PropertyHash()
+{
+ if (!d->refCount.deref())
+ delete d;
+}
+
+struct InternalClassTransition
+{
+ Identifier *id;
+ int flags;
+
+ bool operator==(const InternalClassTransition &other) const
+ { return id == other.id && flags == other.flags; }
+};
+uint qHash(const QV4::InternalClassTransition &t, uint = 0);
+
+struct InternalClass {
+ ExecutionEngine *engine;
+ PropertyHash propertyTable; // id to valueIndex
+ QVector<String *> nameMap;
+
+ QVector<PropertyAttributes> propertyData;
+
+ typedef InternalClassTransition Transition;
+ QHash<Transition, InternalClass *> transitions; // id to next class, positive means add, negative delete
+
+ InternalClass *m_sealed;
+ InternalClass *m_frozen;
+
+ uint size;
+
+ InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0);
+ InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0);
+ void removeMember(Object *object, Identifier *id);
+ uint find(String *s);
+
+ InternalClass *sealed();
+ InternalClass *frozen();
+
+ void destroy();
+
+private:
+ friend struct ExecutionEngine;
+ InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {}
+ InternalClass(const InternalClass &other);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4ir.cpp b/src/qml/qml/v4/qv4ir.cpp
deleted file mode 100644
index 3159cf0d5f..0000000000
--- a/src/qml/qml/v4/qv4ir.cpp
+++ /dev/null
@@ -1,919 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4ir_p.h"
-
-#include <QtCore/qtextstream.h>
-#include <QtCore/qdebug.h>
-#include <math.h>
-
-QT_BEGIN_NAMESPACE
-
-namespace QQmlJS {
-namespace IR {
-
-const char *typeName(Type t)
-{
- switch (t) {
- case InvalidType: return "invalid";
- case UndefinedType: return "undefined";
- case NullType: return "null";
- case VoidType: return "void";
- case StringType: return "string";
- case UrlType: return "QUrl";
- case ColorType: return "QColor";
- case SGAnchorLineType: return "SGAnchorLine";
- case AttachType: return "AttachType";
- case ObjectType: return "object";
- case VariantType: return "variant";
- case VarType: return "var";
- case JSValueType: return "QJSValue";
- case BoolType: return "bool";
- case IntType: return "int";
- case FloatType: return "float";
- case NumberType: return "number";
- default: return "invalid";
- }
-}
-
-inline bool isNumberType(IR::Type ty)
-{
- return ty >= IR::FirstNumberType;
-}
-
-inline bool isStringType(IR::Type ty)
-{
- return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType;
-}
-
-IR::Type maxType(IR::Type left, IR::Type right)
-{
- if (isStringType(left) && isStringType(right)) {
- // String promotions (url to string) are more specific than
- // identity conversions (AKA left == right). That's because
- // we want to ensure we convert urls to strings in binary
- // expressions.
- return IR::StringType;
- } else if (left == right)
- return left;
- else if (isNumberType(left) && isNumberType(right)) {
- IR::Type ty = qMax(left, right);
- return ty == FloatType ? NumberType : ty; // promote floats
- } else if ((isNumberType(left) && isStringType(right)) ||
- (isNumberType(right) && isStringType(left)))
- return IR::StringType;
- else
- return IR::InvalidType;
-}
-
-bool isRealType(IR::Type type)
-{
- return type == IR::NumberType || type == IR::FloatType;
-}
-
-const char *opname(AluOp op)
-{
- switch (op) {
- case OpInvalid: return "?";
-
- case OpIfTrue: return "(bool)";
- case OpNot: return "!";
- case OpUMinus: return "-";
- case OpUPlus: return "+";
- case OpCompl: return "~";
-
- case OpBitAnd: return "&";
- case OpBitOr: return "|";
- case OpBitXor: return "^";
-
- case OpAdd: return "+";
- case OpSub: return "-";
- case OpMul: return "*";
- case OpDiv: return "/";
- case OpMod: return "%";
-
- case OpLShift: return "<<";
- case OpRShift: return ">>";
- case OpURShift: return ">>>";
-
- case OpGt: return ">";
- case OpLt: return "<";
- case OpGe: return ">=";
- case OpLe: return "<=";
- case OpEqual: return "==";
- case OpNotEqual: return "!=";
- case OpStrictEqual: return "===";
- case OpStrictNotEqual: return "!==";
-
- case OpAnd: return "&&";
- case OpOr: return "||";
-
- 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;
- default: return OpInvalid;
- }
-}
-
-void Const::dump(QTextStream &out)
-{
- out << value;
-}
-
-void String::dump(QTextStream &out)
-{
- out << '"' << escape(value) << '"';
-}
-
-QString String::escape(const QStringRef &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 Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint16 line, quint16 column)
-{
- this->type = type;
- this->base = base;
- this->id = id;
- this->symbol = symbol;
- this->ptr = 0;
- this->property = 0;
- this->storage = MemberStorage;
- this->builtin = NoBuiltinSymbol;
- this->line = line;
- this->column = column;
-
- if (id->length() == 8 && *id == QLatin1String("Math.sin")) {
- builtin = MathSinBultinFunction;
- } else if (id->length() == 8 && *id == QLatin1String("Math.cos")) {
- builtin = MathCosBultinFunction;
- } else if (id->length() == 8 && *id == QLatin1String("Math.abs")) {
- builtin = MathAbsBuiltinFunction;
- } else if (id->length() == 10 && *id == QLatin1String("Math.round")) {
- builtin = MathRoundBultinFunction;
- } else if (id->length() == 10 && *id == QLatin1String("Math.floor")) {
- builtin = MathFloorBultinFunction;
- } else if (id->length() == 9 && *id == QLatin1String("Math.ceil")) {
- builtin = MathCeilBuiltinFunction;
- } else if (id->length() == 8 && *id == QLatin1String("Math.max")) {
- builtin = MathMaxBuiltinFunction;
- } else if (id->length() == 8 && *id == QLatin1String("Math.min")) {
- builtin = MathMinBuiltinFunction;
- } else if (id->length() == 7 && *id == QLatin1String("Math.PI")) {
- builtin = MathPIBuiltinConstant;
- this->type = NumberType;
- }
-}
-
-void Name::dump(QTextStream &out)
-{
- if (base) {
- base->dump(out);
- out << '.';
- }
-
- out << *id;
-}
-
-void Temp::dump(QTextStream &out)
-{
- out << 't' << index;
-}
-
-void Unop::dump(QTextStream &out)
-{
- out << opname(op);
- expr->dump(out);
-}
-
-Type Unop::typeForOp(AluOp op, Expr *expr)
-{
- switch (op) {
- case OpIfTrue: return BoolType;
- case OpNot: return BoolType;
-
- case OpUMinus:
- case OpUPlus:
- case OpCompl:
- return maxType(expr->type, NumberType);
-
- default:
- break;
- }
-
- return InvalidType;
-}
-
-void Binop::dump(QTextStream &out)
-{
- left->dump(out);
- out << ' ' << opname(op) << ' ';
- right->dump(out);
-}
-
-Type Binop::typeForOp(AluOp op, Expr *left, Expr *right)
-{
- if (! (left && right))
- return InvalidType;
-
- switch (op) {
- case OpInvalid:
- return InvalidType;
-
- // unary operators
- case OpIfTrue:
- case OpNot:
- case OpUMinus:
- case OpUPlus:
- case OpCompl:
- return InvalidType;
-
- // bit fields
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- return IntType;
-
- case OpAdd:
- if (left->type == StringType)
- return StringType;
- return NumberType;
-
- case OpSub:
- case OpMul:
- case OpDiv:
- case OpMod:
- return NumberType;
-
- case OpLShift:
- case OpRShift:
- case OpURShift:
- return IntType;
-
- case OpAnd:
- case OpOr:
- return BoolType;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- case OpStrictEqual:
- case OpStrictNotEqual:
- return BoolType;
- } // switch
-
- return InvalidType;
-}
-
-void Call::dump(QTextStream &out)
-{
- base->dump(out);
- out << '(';
- for (ExprList *it = args; it; it = it->next) {
- if (it != args)
- out << ", ";
- it->expr->dump(out);
- }
- out << ')';
-}
-
-Type Call::typeForFunction(Expr *base)
-{
- if (! base)
- return InvalidType;
-
- if (Name *name = base->asName()) {
- switch (name->builtin) {
- case MathSinBultinFunction:
- case MathCosBultinFunction:
- case MathAbsBuiltinFunction: //### type could also be Int if input was Int
- case MathMaxBuiltinFunction:
- case MathMinBuiltinFunction:
- return NumberType;
-
- case MathRoundBultinFunction:
- case MathFloorBultinFunction:
- case MathCeilBuiltinFunction:
- return IntType;
-
- case NoBuiltinSymbol:
- case MathPIBuiltinConstant:
- break;
- }
- } // switch
-
- return InvalidType;
-}
-
-void Exp::dump(QTextStream &out, Mode)
-{
- out << "(void) ";
- expr->dump(out);
- out << ';';
-}
-
-void Move::dump(QTextStream &out, Mode)
-{
- target->dump(out);
- out << " = ";
- if (source->type != target->type)
- out << typeName(source->type) << "_to_" << typeName(target->type) << '(';
- source->dump(out);
- if (source->type != target->type)
- out << ')';
- out << ';';
-}
-
-void Jump::dump(QTextStream &out, Mode mode)
-{
- Q_UNUSED(mode);
- out << "goto " << 'L' << target << ';';
-}
-
-void CJump::dump(QTextStream &out, Mode mode)
-{
- Q_UNUSED(mode);
- out << "if (";
- cond->dump(out);
- out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';';
-}
-
-void Ret::dump(QTextStream &out, Mode)
-{
- out << "return";
- if (expr) {
- out << ' ';
- expr->dump(out);
- }
- out << ';';
-}
-
-Function::~Function()
-{
- qDeleteAll(basicBlocks);
-}
-
-QString *Function::newString(const QString &text)
-{
- return pool->NewString(text);
-}
-
-BasicBlock *Function::newBasicBlock()
-{
- const int index = basicBlocks.size();
- return i(new BasicBlock(this, index));
-}
-
-void Function::dump(QTextStream &out)
-{
- out << "function () {" << endl;
- foreach (BasicBlock *bb, basicBlocks) {
- bb->dump(out);
- }
- out << '}' << endl;
-}
-
-Temp *BasicBlock::TEMP(Type type, int index)
-{
- Temp *e = function->pool->New<Temp>();
- e->init(type, index);
- return e;
-}
-
-Temp *BasicBlock::TEMP(Type type)
-{
- return TEMP(type, function->tempCount++);
-}
-
-Expr *BasicBlock::CONST(Type type, double value)
-{
- Const *e = function->pool->New<Const>();
- e->init(type, value);
- return e;
-}
-
-Expr *BasicBlock::STRING(const QStringRef &value)
-{
- String *e = function->pool->New<String>();
- e->init(value);
- return e;
-}
-
-Name *BasicBlock::NAME(const QString &id, quint16 line, quint16 column)
-{
- return NAME(0, id, line, column);
-}
-
-Name *BasicBlock::NAME(Name *base, const QString &id, quint16 line, quint16 column)
-{
- Name *e = function->pool->New<Name>();
- e->init(base, InvalidType,
- function->newString(id),
- Name::Unbound, line, column);
- return e;
-}
-
-Name *BasicBlock::SYMBOL(Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage,
- quint16 line, quint16 column)
-{
- Name *name = SYMBOL(/*base = */ 0, type, id, meta, property, line, column);
- name->storage = storage;
- return name;
-}
-
-Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage,
- quint16 line, quint16 column)
-{
- Name *name = function->pool->New<Name>();
- name->init(base, type, function->newString(id),
- Name::Property, line, column);
- name->meta = meta;
- name->property = property;
- name->storage = storage;
- return name;
-}
-
-Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property,
- quint16 line, quint16 column)
-{
- Name *name = function->pool->New<Name>();
- name->init(base, type, function->newString(id),
- Name::Property, line, column);
- name->meta = meta;
- name->property = property;
- return name;
-}
-
-Name *BasicBlock::ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint16 line, quint16 column)
-{
- Name *name = function->pool->New<Name>();
- name->init(/*base = */ 0, IR::ObjectType,
- function->newString(id),
- Name::IdObject, line, column);
- name->idObject = object;
- name->property = 0;
- name->storage = Name::IdStorage;
- return name;
-}
-
-Name *BasicBlock::ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage,
- quint16 line, quint16 column)
-{
- Name *name = function->pool->New<Name>();
- name->init(/*base = */ 0, IR::AttachType,
- function->newString(id),
- Name::AttachType, line, column);
- name->declarativeType = attachType;
- name->storage = storage;
- return name;
-}
-
-Name *BasicBlock::SINGLETON_OBJECT(const QString &id, const QQmlMetaObject &meta, Name::Storage storage,
- quint16 line, quint16 column)
-{
- Name *name = function->pool->New<Name>();
- name->init(/*base = */ 0, IR::ObjectType,
- function->newString(id),
- Name::SingletonObject, line, column);
- name->meta = meta;
- name->storage = storage;
- return name;
-}
-
-Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
-{
- Unop *e = function->pool->New<Unop>();
- e->init(op, expr);
- return e;
-}
-
-Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
-{
- if (left && right) {
- if (Const *c1 = left->asConst()) {
- if (Const *c2 = right->asConst()) {
- const IR::Type ty = Binop::typeForOp(op, left, right);
-
- switch (op) {
- case OpAdd: return CONST(ty, c1->value + c2->value);
- case OpAnd: return CONST(ty, c1->value ? c2->value : 0);
- case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value));
- case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value));
- case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value));
- case OpDiv: return CONST(ty, c1->value / c2->value);
- case OpEqual: return CONST(ty, c1->value == c2->value);
- case OpGe: return CONST(ty, c1->value >= c2->value);
- case OpGt: return CONST(ty, c1->value > c2->value);
- case OpLe: return CONST(ty, c1->value <= c2->value);
- case OpLShift: return CONST(ty, int(c1->value) << int(c2->value));
- case OpLt: return CONST(ty, c1->value < c2->value);
- case OpMod: return CONST(ty, ::fmod(c1->value, c2->value));
- case OpMul: return CONST(ty, c1->value * c2->value);
- case OpNotEqual: return CONST(ty, c1->value != c2->value);
- case OpOr: return CONST(ty, c1->value ? c1->value : c2->value);
- case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value));
- case OpStrictEqual: return CONST(ty, c1->value == c2->value);
- case OpStrictNotEqual: return CONST(ty, c1->value != c2->value);
- case OpSub: return CONST(ty, c1->value - c2->value);
- case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value));
-
- case OpIfTrue: // unary ops
- case OpNot:
- case OpUMinus:
- case OpUPlus:
- case OpCompl:
- case OpInvalid:
- break;
- }
- }
- } else if (op == OpAdd) {
- if (String *s1 = left->asString()) {
- if (String *s2 = right->asString()) {
- return STRING(function->newString(s1->value.toString() + s2->value));
- }
- }
- }
- }
-
- Binop *e = function->pool->New<Binop>();
- e->init(op, left, right);
- return e;
-}
-
-Expr *BasicBlock::CALL(Expr *base, ExprList *args)
-{
- Call *e = function->pool->New<Call>();
- e->init(base, args);
- return e;
-}
-
-Stmt *BasicBlock::EXP(Expr *expr)
-{
- Exp *s = function->pool->New<Exp>();
- s->init(expr);
- statements.append(s);
- return s;
-}
-
-Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn)
-{
- Move *s = function->pool->New<Move>();
- s->init(target, source, isMoveForReturn);
- statements.append(s);
- return s;
-}
-
-Stmt *BasicBlock::JUMP(BasicBlock *target)
-{
- if (isTerminated())
- return 0;
-
- Jump *s = function->pool->New<Jump>();
- s->init(target);
- statements.append(s);
- return s;
-}
-
-Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
-{
- if (isTerminated())
- return 0;
-
- CJump *s = function->pool->New<CJump>();
- s->init(cond, iftrue, iffalse);
- statements.append(s);
- return s;
-}
-
-Stmt *BasicBlock::RET(Expr *expr, Type type, quint16 line, quint16 column)
-{
- if (isTerminated())
- return 0;
-
- Ret *s = function->pool->New<Ret>();
- s->init(expr, type, line, column);
- statements.append(s);
- return s;
-}
-
-void BasicBlock::dump(QTextStream &out)
-{
- out << 'L' << this << ':' << endl;
- foreach (Stmt *s, statements) {
- out << '\t';
- s->dump(out);
- out << endl;
- }
-}
-
-#ifdef DEBUG_IR_STRUCTURE
-
-static const char *symbolname(Name::Symbol s)
-{
- switch (s) {
- case Name::Unbound:
- return "Unbound";
- case Name::IdObject:
- return "IdObject";
- case Name::AttachType:
- return "AttachType";
- case Name::SingletonObject:
- return "SingletonObject";
- case Name::Object:
- return "Object";
- case Name::Property:
- return "Property";
- case Name::Slot:
- return "Slot";
- default:
- Q_ASSERT(!"Unreachable");
- return "Unknown";
- }
-}
-
-static const char *storagename(Name::Storage s)
-{
- switch (s) {
- case Name::MemberStorage:
- return "MemberStorage";
- case Name::IdStorage:
- return "IdStorage";
- case Name::RootStorage:
- return "RootStorage";
- case Name::ScopeStorage:
- return "ScopeStorage";
- default:
- Q_ASSERT(!"Unreachable");
- return "UnknownStorage";
- }
-}
-
-IRDump::IRDump()
-: indentSize(0)
-{
-}
-
-void IRDump::inc()
-{
- indentSize++;
- indentData = QByteArray(indentSize * 4, ' ');
-}
-
-void IRDump::dec()
-{
- indentSize--;
- indentData = QByteArray(indentSize * 4, ' ');
-}
-
-void IRDump::dec();
-
-void IRDump::expression(QQmlJS::IR::Expr *e)
-{
- inc();
-
- e->accept(this);
-
- dec();
-}
-
-void IRDump::basicblock(QQmlJS::IR::BasicBlock *b)
-{
- inc();
-
- qWarning().nospace() << indent() << "BasicBlock " << b << " {";
- for (int ii = 0; ii < b->statements.count(); ++ii) {
- statement(b->statements.at(ii));
- if (ii != (b->statements.count() - 1))
- qWarning();
- }
- qWarning().nospace() << indent() << '}';
-
- dec();
-}
-
-void IRDump::statement(QQmlJS::IR::Stmt *s)
-{
- inc();
-
- s->accept(this);
-
- dec();
-}
-
-void IRDump::function(QQmlJS::IR::Function *f)
-{
- inc();
-
- qWarning().nospace() << indent() << "Function {";
- for (int ii = 0; ii < f->basicBlocks.count(); ++ii) {
- basicblock(f->basicBlocks.at(ii));
- }
- qWarning().nospace() << indent() << '}';
-
- dec();
-}
-
-const char *IRDump::indent()
-{
- return indentData.constData();
-}
-
-void IRDump::visitConst(QQmlJS::IR::Const *e)
-{
- qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << '}';
-}
-
-void IRDump::visitString(QQmlJS::IR::String *e)
-{
- qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << '}';
-}
-
-static void namedumprecur(QQmlJS::IR::Name *e, const char *indent)
-{
- if (e->base) namedumprecur(e->base, indent);
- qWarning().nospace() << indent << " { type: " << typeName(e->type) << ", symbol: " << symbolname(e->symbol) << ", storage: " << storagename(e->storage) << ", id: " << e->id << '}';
-}
-
-void IRDump::visitName(QQmlJS::IR::Name *e)
-{
- qWarning().nospace() << indent() << "Name:Expr {";
- namedumprecur(e, indent());
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitTemp(QQmlJS::IR::Temp *e)
-{
- qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }";
-}
-
-void IRDump::visitUnop(QQmlJS::IR::Unop *e)
-{
- qWarning().nospace() << indent() << "Unop:Expr { ";
- qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op);
- qWarning().nospace() << indent() << " expr: {";
- expression(e->expr);
- qWarning().nospace() << indent() << " }";
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitBinop(QQmlJS::IR::Binop *e)
-{
- qWarning().nospace() << indent() << "Binop:Expr { ";
- qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op);
- qWarning().nospace() << indent() << " left: {";
- inc();
- expression(e->left);
- dec();
- qWarning().nospace() << indent() << " },";
- qWarning().nospace() << indent() << " right: {";
- inc();
- expression(e->right);
- dec();
- qWarning().nospace() << indent() << " }";
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitCall(QQmlJS::IR::Call *e)
-{
- Q_UNUSED(e);
- qWarning().nospace() << indent() << "Exp::Call { }";
-}
-
-void IRDump::visitExp(QQmlJS::IR::Exp *s)
-{
- qWarning().nospace() << indent() << "Exp:Stmt {";
- expression(s->expr);
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitMove(QQmlJS::IR::Move *s)
-{
- qWarning().nospace() << indent() << "Move:Stmt {";
- qWarning().nospace() << indent() << " isMoveForReturn: " << s->isMoveForReturn;
- qWarning().nospace() << indent() << " target: {";
- inc();
- expression(s->target);
- dec();
- qWarning().nospace() << indent() << " },";
- qWarning().nospace() << indent() << " source: {";
- inc();
- expression(s->source);
- dec();
- qWarning().nospace() << indent() << " }";
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitJump(QQmlJS::IR::Jump *s)
-{
- qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }";
-}
-
-void IRDump::visitCJump(QQmlJS::IR::CJump *s)
-{
- qWarning().nospace() << indent() << "CJump:Stmt {";
- qWarning().nospace() << indent() << " cond: {";
- inc();
- expression(s->cond);
- dec();
- qWarning().nospace() << indent() << " }";
- qWarning().nospace() << indent() << " iftrue: BasicBlock(" << s->iftrue << ')';
- qWarning().nospace() << indent() << " iffalse: BasicBlock(" << s->iffalse << ')';
- qWarning().nospace() << indent() << '}';
-}
-
-void IRDump::visitRet(QQmlJS::IR::Ret *s)
-{
- qWarning().nospace() << indent() << "Ret:Stmt {";
- qWarning().nospace() << indent() << " type: " << typeName(s->type);
- expression(s->expr);
- qWarning().nospace() << indent() << '}';
-}
-#endif
-
-} // end of namespace IR
-} // end of namespace QQmlJS
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h
deleted file mode 100644
index 701f76d9e4..0000000000
--- a/src/qml/qml/v4/qv4ir_p.h
+++ /dev/null
@@ -1,615 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4IR_P_H
-#define QV4IR_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/qqmljsast_p.h>
-#include <private/qqmljsengine_p.h>
-#include <private/qqmlscript_p.h>
-#include <private/qqmlimport_p.h>
-#include <private/qqmlengine_p.h>
-#include <private/qv4compiler_p.h>
-
-#include <private/qqmlpool_p.h>
-#include <QtCore/qvarlengtharray.h>
-
-// #define DEBUG_IR_STRUCTURE
-
-#ifdef CONST
-# undef CONST
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QTextStream;
-class QQmlType;
-
-namespace QQmlJS {
-
-namespace IR {
-
-struct BasicBlock;
-struct Function;
-
-struct Stmt;
-struct Expr;
-
-// expressions
-struct Const;
-struct String;
-struct Name;
-struct Temp;
-struct Unop;
-struct Binop;
-struct Call;
-
-// statements
-struct Exp;
-struct Move;
-struct Jump;
-struct CJump;
-struct Ret;
-
-enum AluOp {
- OpInvalid = 0,
-
- OpIfTrue,
- OpNot,
- OpUMinus,
- OpUPlus,
- OpCompl,
-
- OpBitAnd,
- OpBitOr,
- OpBitXor,
-
- OpAdd,
- OpSub,
- OpMul,
- OpDiv,
- OpMod,
-
- OpLShift,
- OpRShift,
- OpURShift,
-
- OpGt,
- OpLt,
- OpGe,
- OpLe,
- OpEqual,
- OpNotEqual,
- OpStrictEqual,
- OpStrictNotEqual,
-
- OpAnd,
- OpOr
-};
-AluOp binaryOperator(int op);
-
-enum Type {
- InvalidType,
- UndefinedType,
- NullType,
- VoidType,
- StringType,
- UrlType,
- ColorType,
- SGAnchorLineType,
- AttachType,
- ObjectType,
- VariantType,
- VarType,
- JSValueType,
-
- FirstNumberType,
- BoolType = FirstNumberType,
- IntType,
- FloatType,
- NumberType
-};
-Type maxType(IR::Type left, IR::Type right);
-bool isRealType(IR::Type type);
-const char *typeName(IR::Type t);
-
-struct ExprVisitor {
- virtual ~ExprVisitor() {}
- virtual void visitConst(Const *) {}
- virtual void visitString(String *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitUnop(Unop *) {}
- virtual void visitBinop(Binop *) {}
- virtual void visitCall(Call *) {}
-};
-
-struct StmtVisitor {
- virtual ~StmtVisitor() {}
- virtual void visitExp(Exp *) {}
- virtual void visitMove(Move *) {}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *) {}
- virtual void visitRet(Ret *) {}
-};
-
-struct Expr: QQmlPool::POD {
- Type type;
-
- Expr(): type(InvalidType) {}
- virtual ~Expr() {}
- virtual void accept(ExprVisitor *) = 0;
- virtual Const *asConst() { return 0; }
- virtual String *asString() { return 0; }
- virtual Name *asName() { return 0; }
- virtual Temp *asTemp() { return 0; }
- virtual Unop *asUnop() { return 0; }
- virtual Binop *asBinop() { return 0; }
- virtual Call *asCall() { return 0; }
- virtual void dump(QTextStream &out) = 0;
-};
-
-struct ExprList: QQmlPool::POD {
- Expr *expr;
- ExprList *next;
-
- void init(Expr *expr, ExprList *next = 0)
- {
- this->expr = expr;
- this->next = next;
- }
-};
-
-struct Const: Expr {
- double value;
-
- void init(Type type, double value)
- {
- this->type = type;
- this->value = value;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitConst(this); }
- virtual Const *asConst() { return this; }
-
- virtual void dump(QTextStream &out);
-};
-
-struct String: Expr {
- QStringRef value;
-
- void init(const QStringRef &value)
- {
- this->type = StringType;
- this->value = value;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitString(this); }
- virtual String *asString() { return this; }
-
- virtual void dump(QTextStream &out);
- static QString escape(const QStringRef &s);
-};
-
-enum BuiltinSymbol {
- NoBuiltinSymbol,
- MathSinBultinFunction,
- MathCosBultinFunction,
- MathRoundBultinFunction,
- MathFloorBultinFunction,
- MathCeilBuiltinFunction,
- MathAbsBuiltinFunction,
- MathMaxBuiltinFunction,
- MathMinBuiltinFunction,
-
- MathPIBuiltinConstant
-};
-
-struct Name: Expr {
- enum Symbol {
- Unbound,
- IdObject, // This is a load of a id object. Storage will always be IdStorage
- AttachType, // This is a load of an attached object
- SingletonObject, // This is a load of a singleton object
- Object, // XXX what is this for?
- Property, // This is a load of a regular property
- Slot // XXX what is this for?
- };
-
- enum Storage {
- MemberStorage, // This is a property of a previously fetched object
- IdStorage, // This is a load of a id object. Symbol will always be IdObject
- RootStorage, // This is a property of the root object
- ScopeStorage // This is a property of the scope object
- };
-
- Name *base;
- const QString *id;
- Symbol symbol;
- union {
- void *ptr;
- const QQmlType *declarativeType;
- const QQmlScript::Object *idObject;
- };
-
- QQmlMetaObject meta;
- QQmlPropertyData *property;
- Storage storage;
- BuiltinSymbol builtin;
- quint16 line;
- quint16 column;
-
- void init(Name *base, Type type, const QString *id, Symbol symbol, quint16 line, quint16 column);
-
- inline bool is(Symbol s) const { return s == symbol; }
- inline bool isNot(Symbol s) const { return s != symbol; }
-
- virtual void accept(ExprVisitor *v) { v->visitName(this); }
- virtual Name *asName() { return this; }
-
- virtual void dump(QTextStream &out);
-};
-
-struct Temp: Expr {
- int index;
-
- void init(Type type, int index)
- {
- this->type = type;
- this->index = index;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitTemp(this); }
- virtual Temp *asTemp() { return this; }
-
- virtual void dump(QTextStream &out);
-};
-
-struct Unop: Expr {
- AluOp op;
- Expr *expr;
-
- void init(AluOp op, Expr *expr)
- {
- this->type = this->typeForOp(op, expr);
- this->op = op;
- this->expr = expr;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitUnop(this); }
- virtual Unop *asUnop() { return this; }
-
- virtual void dump(QTextStream &out);
-
-private:
- static Type typeForOp(AluOp op, Expr *expr);
-};
-
-struct Binop: Expr {
- AluOp op;
- Expr *left;
- Expr *right;
-
- void init(AluOp op, Expr *left, Expr *right)
- {
- this->type = typeForOp(op, left, right);
- this->op = op;
- this->left = left;
- this->right = right;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitBinop(this); }
- virtual Binop *asBinop() { return this; }
-
- virtual void dump(QTextStream &out);
-
- static Type typeForOp(AluOp op, Expr *left, Expr *right);
-};
-
-struct Call: Expr {
- Expr *base;
- ExprList *args;
-
- void init(Expr *base, ExprList *args)
- {
- this->type = typeForFunction(base);
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- virtual void accept(ExprVisitor *v) { v->visitCall(this); }
- virtual Call *asCall() { return this; }
-
- virtual void dump(QTextStream &out);
-
-private:
- static Type typeForFunction(Expr *base);
-};
-
-struct Stmt: QQmlPool::POD {
- enum Mode {
- HIR,
- MIR
- };
-
- virtual ~Stmt() {}
- virtual Stmt *asTerminator() { return 0; }
-
- virtual void accept(StmtVisitor *) = 0;
- virtual Exp *asExp() { return 0; }
- virtual Move *asMove() { return 0; }
- virtual Jump *asJump() { return 0; }
- virtual CJump *asCJump() { return 0; }
- virtual Ret *asRet() { return 0; }
- virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
-};
-
-struct Exp: Stmt {
- Expr *expr;
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- virtual void accept(StmtVisitor *v) { v->visitExp(this); }
- virtual Exp *asExp() { return this; }
-
- virtual void dump(QTextStream &out, Mode);
-};
-
-struct Move: Stmt {
- Expr *target;
- Expr *source;
- bool isMoveForReturn;
-
- void init(Expr *target, Expr *source, bool isMoveForReturn)
- {
- this->target = target;
- this->source = source;
- this->isMoveForReturn = isMoveForReturn;
- }
-
- virtual void accept(StmtVisitor *v) { v->visitMove(this); }
- virtual Move *asMove() { return this; }
-
- virtual void dump(QTextStream &out, Mode);
-};
-
-struct Jump: Stmt {
- BasicBlock *target;
-
- void init(BasicBlock *target)
- {
- this->target = target;
- }
-
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitJump(this); }
- virtual Jump *asJump() { return this; }
-
- virtual void dump(QTextStream &out, Mode mode);
-};
-
-struct CJump: Stmt {
- Expr *cond;
- BasicBlock *iftrue;
- BasicBlock *iffalse;
-
- void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
- {
- this->cond = cond;
- this->iftrue = iftrue;
- this->iffalse = iffalse;
- }
-
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitCJump(this); }
- virtual CJump *asCJump() { return this; }
-
- virtual void dump(QTextStream &out, Mode mode);
-};
-
-struct Ret: Stmt {
- Expr *expr;
- Type type;
- quint16 line;
- quint16 column;
-
- void init(Expr *expr, Type type, quint16 line, quint16 column)
- {
- this->expr = expr;
- this->type = type;
- this->line = line;
- this->column = column;
- }
-
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitRet(this); }
- virtual Ret *asRet() { return this; }
-
- virtual void dump(QTextStream &out, Mode);
-};
-
-struct Function {
- QQmlPool *pool;
- QVarLengthArray<BasicBlock *, 8> basicBlocks;
- int tempCount;
-
- Function(QQmlPool *pool)
- : pool(pool), tempCount(0) {}
-
- virtual ~Function();
-
- BasicBlock *newBasicBlock();
- QString *newString(const QString &text);
-
- inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; }
-
- virtual void dump(QTextStream &out);
-};
-
-struct BasicBlock {
- Function *function;
- int index;
- int offset;
- QVarLengthArray<Stmt *, 32> statements;
-
- BasicBlock(Function *function, int index): function(function), index(index), offset(-1) {}
- ~BasicBlock() {}
-
- template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; }
-
- inline bool isEmpty() const {
- return statements.isEmpty();
- }
-
- inline Stmt *terminator() const {
- if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0)
- return statements.at(statements.size() - 1);
- return 0;
- }
-
- inline bool isTerminated() const {
- if (terminator() != 0)
- return true;
- return false;
- }
-
- Temp *TEMP(Type type, int index);
- Temp *TEMP(Type type);
-
- Expr *CONST(Type type, double value);
- Expr *STRING(const QStringRef &value);
-
- Name *NAME(const QString &id, quint16 line, quint16 column);
- Name *NAME(Name *base, const QString &id, quint16 line, quint16 column);
- Name *SYMBOL(Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, quint16 line, quint16 column);
- Name *SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, quint16 line, quint16 column);
- Name *SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, quint16 line, quint16 column);
- Name *ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint16 line, quint16 column);
- Name *ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage, quint16 line, quint16 column);
- Name *SINGLETON_OBJECT(const QString &id, const QQmlMetaObject &meta, Name::Storage storage, quint16 line, quint16 column);
-
- Expr *UNOP(AluOp op, Expr *expr);
- Expr *BINOP(AluOp op, Expr *left, Expr *right);
- Expr *CALL(Expr *base, ExprList *args);
-
- Stmt *EXP(Expr *expr);
- Stmt *MOVE(Expr *target, Expr *source, bool = false);
-
- Stmt *JUMP(BasicBlock *target);
- Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
- Stmt *RET(Expr *expr, Type type, quint16 line, quint16 column);
-
- void dump(QTextStream &out);
-};
-
-#ifdef DEBUG_IR_STRUCTURE
-struct IRDump : public ExprVisitor,
- public StmtVisitor
-{
-public:
- IRDump();
-
- void expression(QQmlJS::IR::Expr *);
- void basicblock(QQmlJS::IR::BasicBlock *);
- void statement(QQmlJS::IR::Stmt *);
- void function(QQmlJS::IR::Function *);
-protected:
-
- const char *indent();
-
- //
- // expressions
- //
- virtual void visitConst(QQmlJS::IR::Const *e);
- virtual void visitString(QQmlJS::IR::String *e);
- virtual void visitName(QQmlJS::IR::Name *e);
- virtual void visitTemp(QQmlJS::IR::Temp *e);
- virtual void visitUnop(QQmlJS::IR::Unop *e);
- virtual void visitBinop(QQmlJS::IR::Binop *e);
- virtual void visitCall(QQmlJS::IR::Call *e);
-
- //
- // statements
- //
- virtual void visitExp(QQmlJS::IR::Exp *s);
- virtual void visitMove(QQmlJS::IR::Move *s);
- virtual void visitJump(QQmlJS::IR::Jump *s);
- virtual void visitCJump(QQmlJS::IR::CJump *s);
- virtual void visitRet(QQmlJS::IR::Ret *s);
-
-private:
- int indentSize;
- QByteArray indentData;
- void inc();
- void dec();
-};
-#endif
-
-} // end of namespace IR
-
-} // end of namespace QQmlJS
-
-QT_END_NAMESPACE
-
-#endif // QV4IR_P_H
diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp
deleted file mode 100644
index d217b5e2f5..0000000000
--- a/src/qml/qml/v4/qv4irbuilder.cpp
+++ /dev/null
@@ -1,1394 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4irbuilder_p.h"
-#include "qv4compiler_p_p.h"
-
-#include <private/qqmlglobal_p.h>
-#include <private/qqmlmetatype_p.h>
-#include <private/qqmltypenamecache_p.h>
-
-DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
-
-QT_BEGIN_NAMESPACE
-
-using namespace QQmlJS;
-
-static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *engine)
-{
- switch (t) {
- case QMetaType::Bool:
- return IR::BoolType;
-
- case QMetaType::Int:
- return IR::IntType;
-
- case QMetaType::Float:
- return IR::FloatType;
-
- case QMetaType::Double:
- return IR::NumberType;
-
- case QMetaType::QString:
- return IR::StringType;
-
- case QMetaType::QUrl:
- return IR::UrlType;
-
- case QMetaType::QColor:
- return IR::ColorType;
-
- default:
- if (t == QQmlMetaType::QQuickAnchorLineMetaTypeId()) {
- return IR::SGAnchorLineType;
- } else if (!engine->metaObjectForType(t).isNull()) {
- return IR::ObjectType;
- } else if (t == qMetaTypeId<QJSValue>()) {
- return IR::JSValueType;
- }
-
- return IR::InvalidType;
- }
-}
-
-QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr,
- QQmlEnginePrivate *engine)
-: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false),
- _invalidatable(false)
-{
-}
-
-bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function,
- QQmlJS::AST::Node *ast, bool *invalidatable)
-{
- bool discarded = false;
-
- IR::BasicBlock *block = function->newBasicBlock();
-
- qSwap(_discard, discarded);
- qSwap(_function, function);
- qSwap(_block, block);
-
- ExprResult r;
- AST::SourceLocation location;
- if (AST::ExpressionNode *asExpr = ast->expressionCast()) {
- r = expression(asExpr);
- location = asExpr->firstSourceLocation();
- } else if (AST::Statement *asStmt = ast->statementCast()) {
- r = statement(asStmt);
- location = asStmt->firstSourceLocation();
- }
-
- //_block->MOVE(_block->TEMP(IR::InvalidType), r.code);
- if (r.code) {
- IR::Type targetType = IR::InvalidType;
-
- // This is the only operation where variant is supported:
- QQmlPropertyData *data = &m_expression->property->core;
- if (data->propType == QMetaType::QVariant) {
- targetType = (data->isVarProperty() ? IR::VarType : IR::VariantType);
- } else {
- targetType = irTypeFromVariantType(data->propType, m_engine);
- }
-
- if (targetType != r.type()) {
- IR::Expr *x = _block->TEMP(targetType);
- _block->MOVE(x, r, true);
- r.code = x;
- }
- _block->RET(r.code, targetType, location.startLine, location.startColumn);
- }
-
- qSwap(_block, block);
- qSwap(_function, function);
- qSwap(_discard, discarded);
-
- *invalidatable = _invalidatable;
- return !discarded;
-}
-
-bool QV4IRBuilder::buildName(QList<QStringRef> &name,
- AST::Node *node,
- QList<AST::ExpressionNode *> *nodes)
-{
- if (node->kind == AST::Node::Kind_IdentifierExpression) {
- name << static_cast<AST::IdentifierExpression*>(node)->name;
- if (nodes) *nodes << static_cast<AST::IdentifierExpression*>(node);
- } else if (node->kind == AST::Node::Kind_FieldMemberExpression) {
- AST::FieldMemberExpression *expr =
- static_cast<AST::FieldMemberExpression *>(node);
-
- if (!buildName(name, expr->base, nodes))
- return false;
-
- name << expr->name;
- if (nodes) *nodes << expr;
- } else {
- return false;
- }
-
- return true;
-}
-
-void QV4IRBuilder::discard()
-{
- _discard = true;
-}
-
-QV4IRBuilder::ExprResult
-QV4IRBuilder::expression(AST::ExpressionNode *ast)
-{
- ExprResult r;
- if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
-
- if (r.is(IR::InvalidType))
- discard();
- else {
- Q_ASSERT(r.hint == r.format);
- }
- }
-
- return r;
-}
-
-void QV4IRBuilder::condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
-{
- if (! ast)
- return;
- ExprResult r(iftrue, iffalse);
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
-
- if (r.format != ExprResult::cx) {
- if (! r.code)
- discard();
-
- Q_ASSERT(r.hint == ExprResult::cx);
- Q_ASSERT(r.format == ExprResult::ex);
-
- if (r.type() != IR::BoolType) {
- IR::Temp *t = _block->TEMP(IR::BoolType);
- _block->MOVE(t, r);
- r = t;
- }
-
- _block->CJUMP(_block->UNOP(IR::OpIfTrue, r), iftrue, iffalse);
- }
-}
-
-QV4IRBuilder::ExprResult
-QV4IRBuilder::statement(AST::Statement *ast)
-{
- ExprResult r;
- if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
-
- if (r.is(IR::InvalidType))
- discard();
- else {
- Q_ASSERT(r.hint == r.format);
- }
- }
-
- return r;
-}
-
-void QV4IRBuilder::sourceElement(AST::SourceElement *ast)
-{
- accept(ast);
-}
-
-void QV4IRBuilder::implicitCvt(ExprResult &expr, IR::Type type)
-{
- if (expr.type() == type)
- return; // nothing to do
-
- IR::Expr *x = _block->TEMP(type);
- _block->MOVE(x, expr.code);
- expr.code = x;
-}
-
-// QML
-bool QV4IRBuilder::visit(AST::UiProgram *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiImportList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiImport *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiPublicMember *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiSourceElement *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiObjectDefinition *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiObjectInitializer *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiObjectBinding *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiScriptBinding *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiArrayBinding *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiObjectMemberList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiArrayMemberList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UiQualifiedId *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-
-// JS
-bool QV4IRBuilder::visit(AST::Program *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::SourceElements *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FunctionSourceElement *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::StatementSourceElement *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-// object literals
-bool QV4IRBuilder::visit(AST::PropertyAssignmentList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PropertyNameAndValue *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PropertyGetterSetter *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::IdentifierPropertyName *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::StringLiteralPropertyName *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NumericLiteralPropertyName *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-
-// array literals
-bool QV4IRBuilder::visit(AST::ElementList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::Elision *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-
-// function calls
-bool QV4IRBuilder::visit(AST::ArgumentList *)
-{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-// expressions
-bool QV4IRBuilder::visit(AST::ObjectLiteral *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ArrayLiteral *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ThisExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::IdentifierExpression *ast)
-{
- const quint16 line = ast->identifierToken.startLine;
- const quint16 column = ast->identifierToken.startColumn;
-
- const QString name = ast->name.toString();
-
- if (name.at(0) == QLatin1Char('u') && name.length() == 9 && name == QLatin1String("undefined")) {
- _expr.code = _block->CONST(IR::UndefinedType, 0); // ### undefined value
- } else if (m_engine->v8engine()->illegalNames().contains(name) ) {
- if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name;
- return false;
- } else if (const QQmlScript::Object *obj = m_expression->ids->value(name)) {
- IR::Name *code = _block->ID_OBJECT(name, obj, line, column);
- if (obj == m_expression->component)
- code->storage = IR::Name::RootStorage;
- _expr.code = code;
- } else {
-
- QQmlTypeNameCache::Result r = m_expression->importCache->query(name);
- if (r.isValid()) {
- if (r.type) {
- if (r.type->isSingleton()) {
- // Note: we don't need to check singletonType->qobjectCallback here, since
- // we did that check in registerSingletonType() in qqmlmetatype.cpp.
- // We cannot create the QObject Singleton Type Instance here,
- // as we might be running in a loader thread.
- // Thus, V4 can only handle bindings which use Singleton Types which
- // were registered with the templated registration function.
- _expr.code = _block->SINGLETON_OBJECT(name, r.type->singletonInstanceInfo()->instanceMetaObject, IR::Name::MemberStorage, line, column);
- } else {
- _expr.code = _block->ATTACH_TYPE(name, r.type, IR::Name::ScopeStorage, line, column);
- }
- }
- } else {
- bool found = false;
-
- if (m_expression->context != m_expression->component) {
- // RootStorage is more efficient than ScopeStorage, so prefer that if they are the same
- QQmlPropertyCache *cache = m_expression->context->synthCache;
- if (!cache) cache = m_expression->context->metatype;
-
- QQmlPropertyData *data = cache->property(name, 0, 0);
-
- if (data && data->hasRevision()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** versioned symbol:" << name;
- discard();
- return false;
- }
-
- if (data && !data->isFunction()) {
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(irType, name, QQmlMetaObject(cache), data, IR::Name::ScopeStorage, line, column);
- found = true;
- }
- }
-
- if (!found) {
- QQmlPropertyCache *cache = m_expression->component->synthCache;
- if (!cache) cache = m_expression->component->metatype;
-
- QQmlPropertyData *data = cache->property(name, 0, 0);
-
- if (data && data->hasRevision()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** versioned symbol:" << name;
- discard();
- return false;
- }
-
- if (data && !data->isFunction()) {
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(irType, name, QQmlMetaObject(cache), data, IR::Name::RootStorage, line, column);
- found = true;
- }
- }
-
- if (!found && qmlVerboseCompiler())
- qWarning() << "*** unknown symbol:" << name;
- }
- }
-
- if (_expr.code && _expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
-
- if (_expr.type() != IR::BoolType) {
- IR::Temp *t = _block->TEMP(IR::BoolType);
- _block->MOVE(t, _expr);
- _expr.code = t;
- }
-
- _block->CJUMP(_expr.code, _expr.iftrue, _expr.iffalse);
- _expr.code = 0;
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NullExpression *)
-{
- // ### TODO: cx format
- _expr.code = _block->CONST(IR::NullType, 0);
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::TrueLiteral *)
-{
- // ### TODO: cx format
- _expr.code = _block->CONST(IR::BoolType, 1);
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FalseLiteral *)
-{
- // ### TODO: cx format
- _expr.code = _block->CONST(IR::BoolType, 0);
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::StringLiteral *ast)
-{
- // ### TODO: cx format
- _expr.code = _block->STRING(ast->value);
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NumericLiteral *ast)
-{
- if (_expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
- _block->JUMP(ast->value ? _expr.iftrue : _expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::NumberType, ast->value);
- }
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::RegExpLiteral *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NestedExpression *)
-{
- return true; // the value of the nested expression
-}
-
-bool QV4IRBuilder::visit(AST::ArrayMemberExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast)
-{
- if (IR::Expr *left = expression(ast->base)) {
- if (IR::Name *baseName = left->asName()) {
- const quint32 line = ast->identifierToken.startLine;
- const quint32 column = ast->identifierToken.startColumn;
-
- QString name = ast->name.toString();
-
- switch(baseName->symbol) {
- case IR::Name::Unbound:
- break;
-
- case IR::Name::AttachType:
- if (name.at(0).isUpper()) {
- QByteArray utf8Name = name.toUtf8();
- const char *enumName = utf8Name.constData();
-
- const QMetaObject *meta = baseName->declarativeType->metaObject();
- bool found = false;
- for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) {
- QMetaEnum e = meta->enumerator(ii);
- for (int jj = 0; !found && jj < e.keyCount(); ++jj) {
- if (0 == strcmp(e.key(jj), enumName)) {
- found = true;
- _expr.code = _block->CONST(IR::IntType, e.value(jj));
- }
- }
- }
-
- if (!found && qmlVerboseCompiler())
- qWarning() << "*** unresolved enum:"
- << (*baseName->id + QLatin1Char('.') + ast->name.toString());
- } else if(const QMetaObject *attachedMeta = baseName->declarativeType->attachedPropertiesType()) {
- QQmlPropertyCache *cache = m_engine->cache(attachedMeta);
- QQmlPropertyData *data = cache->property(name, 0, 0);
-
- if (!data || data->isFunction())
- return false; // Don't support methods (or non-existing properties ;)
-
- if (!data->isFinal())
- _invalidatable = true;
-
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data, line, column);
- }
- break;
-
- case IR::Name::SingletonObject: {
- if (name.at(0).isUpper()) {
- QByteArray utf8Name = name.toUtf8();
- const char *enumName = utf8Name.constData();
-
- const QQmlPropertyCache *cache = baseName->meta.propertyCache(m_engine);
- if (!cache) {
- //Happens in some cases where they make properties with uppercase names
- qFatal("QV4: Unable to resolve enum: '%s'",
- QString(*baseName->id + QLatin1Char('.') + ast->name.toString()).toLatin1().constData());
- }
-
- const QMetaObject *meta = cache->firstCppMetaObject();
- bool found = false;
- for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) {
- QMetaEnum e = meta->enumerator(ii);
- for (int jj = 0; !found && jj < e.keyCount(); ++jj) {
- if (0 == strcmp(e.key(jj), enumName)) {
- found = true;
- _expr.code = _block->CONST(IR::IntType, e.value(jj));
- }
- }
- }
- if (!found && qmlVerboseCompiler())
- qWarning() << "*** unresolved enum:"
- << (*baseName->id + QLatin1Char('.') + ast->name.toString());
- } else {
- QQmlPropertyCache *cache = baseName->meta.propertyCache(m_engine);
- if (!cache) return false;
- QQmlPropertyData *data = cache->property(name, 0, 0);
-
- if (!data || data->isFunction())
- return false; // Don't support methods (or non-existing properties ;)
-
- if (!data->isFinal())
- _invalidatable = true;
-
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(baseName, irType, name, baseName->meta, data, line, column);
- }
- }
- break;
-
- case IR::Name::IdObject: {
- const QQmlScript::Object *idObject = baseName->idObject;
- QQmlPropertyCache *cache =
- idObject->synthCache?idObject->synthCache:idObject->metatype;
-
- QQmlPropertyData *data = cache->property(name, 0, 0);
-
- if (!data || data->isFunction())
- return false; // Don't support methods (or non-existing properties ;)
-
- if (data->hasRevision()) {
- if (qmlVerboseCompiler())
- qWarning() << "*** versioned symbol:" << name;
- discard();
- return false;
- }
-
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(baseName, irType, name, QQmlMetaObject(cache), data, line, column);
- }
- break;
-
- case IR::Name::Property:
- if (baseName->type == IR::ObjectType && !baseName->meta.isNull()) {
- QQmlMetaObject meta = m_engine->metaObjectForType(baseName->property->propType);
- QQmlPropertyCache *cache = meta.propertyCache(m_engine);
- if (!cache)
- return false;
-
- if (QQmlPropertyData *data = cache->property(name, 0, 0)) {
- if (!baseName->property->isFinal() || !data->isFinal())
- _invalidatable = true;
-
- IR::Type irType = irTypeFromVariantType(data->propType, m_engine);
- _expr.code = _block->SYMBOL(baseName, irType, name,
- meta, data, line, column);
- }
- }
- break;
-
- case IR::Name::Object:
- case IR::Name::Slot:
- break;
- }
- }
- }
-
- return false;
-}
-
-bool QV4IRBuilder::preVisit(AST::Node *)
-{
- return ! _discard;
-}
-
-bool QV4IRBuilder::visit(AST::NewMemberExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NewExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::CallExpression *ast)
-{
- QList<QStringRef> names;
- QList<AST::ExpressionNode *> nameNodes;
-
- names.reserve(4);
- nameNodes.reserve(4);
-
- if (buildName(names, ast->base, &nameNodes)) {
- //ExprResult base = expression(ast->base);
- QString id;
- for (int i = 0; i < names.size(); ++i) {
- if (i)
- id += QLatin1Char('.');
- id += names.at(i);
- }
- const AST::SourceLocation loc = nameNodes.last()->firstSourceLocation();
- IR::Expr *base = _block->NAME(id, loc.startLine, loc.startColumn);
-
- IR::ExprList *args = 0, **argsInserter = &args;
- for (AST::ArgumentList *it = ast->arguments; it; it = it->next) {
- IR::Expr *arg = expression(it->expression);
- *argsInserter = _function->pool->New<IR::ExprList>();
- (*argsInserter)->init(arg);
- argsInserter = &(*argsInserter)->next;
- }
-
- IR::Temp *r = _block->TEMP(IR::InvalidType);
- IR::Expr *call = _block->CALL(base, args);
- _block->MOVE(r, call);
- r->type = call->type;
- _expr.code = r;
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PostIncrementExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PostDecrementExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::DeleteExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::VoidExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::TypeOfExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PreIncrementExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::PreDecrementExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UnaryPlusExpression *ast)
-{
- ExprResult expr = expression(ast->expression);
- if (expr.isNot(IR::InvalidType)) {
- if (expr.code->asConst() != 0) {
- _expr = expr;
- return false;
- }
-
- IR::Expr *code = _block->UNOP(IR::OpUPlus, expr);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr, code);
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::UnaryMinusExpression *ast)
-{
- ExprResult expr = expression(ast->expression);
- if (expr.isNot(IR::InvalidType)) {
- if (IR::Const *c = expr.code->asConst()) {
- _expr = expr;
- _expr.code = _block->CONST(expr->type, -c->value);
- return false;
- }
-
- IR::Expr *code = _block->UNOP(IR::OpUMinus, expr);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr, code);
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::TildeExpression *ast)
-{
- ExprResult expr = expression(ast->expression);
- if (expr.isNot(IR::InvalidType)) {
- if (IR::Const *c = expr.code->asConst()) {
- _expr = expr;
- _expr.code = _block->CONST(expr->type, ~int(c->value));
- return false;
- }
- IR::Expr *code = _block->UNOP(IR::OpCompl, expr);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr, code);
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::NotExpression *ast)
-{
- ExprResult expr = expression(ast->expression);
-
- if (expr.isNot(IR::InvalidType)) {
- if (IR::Const *c = expr.code->asConst()) {
- _expr = expr;
- _expr.code = _block->CONST(IR::BoolType, !c->value);
- return false;
- }
-
- IR::Expr *code = _block->UNOP(IR::OpNot, expr);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr, code);
-
- } else if (expr.hint == ExprResult::cx) {
- expr.format = ExprResult::cx;
- _block->CJUMP(_block->UNOP(IR::OpNot, expr), _expr.iftrue, _expr.iffalse);
- return false;
- }
-
- return false;
-}
-
-void QV4IRBuilder::binop(AST::BinaryExpression *ast, ExprResult left, ExprResult right)
-{
- if (IR::Type t = maxType(left.type(), right.type())) {
- if (!left->asConst() && !right->asConst()) {
- // the implicit conversions are needed only
- // when compiling non-constant expressions.
- implicitCvt(left, t);
- implicitCvt(right, t);
- }
- } else if ((left.type() != IR::ObjectType && left.type() != IR::NullType) ||
- (right.type() != IR::ObjectType && right.type() != IR::NullType))
- return;
-
- if (_expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
- _block->CJUMP(_block->BINOP(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse);
- } else {
- IR::Expr *e = _block->BINOP(IR::binaryOperator(ast->op), left, right);
- if (e->asConst() != 0 || e->asString() != 0)
- _expr.code = e;
- else {
- IR::Temp *t = _block->TEMP(e->type);
- _block->MOVE(t, e);
- _expr.code = t;
- }
- }
-}
-
-bool QV4IRBuilder::visit(AST::BinaryExpression *ast)
-{
- switch (ast->op) {
- case QSOperator::And: {
- if (_expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
-
- Q_ASSERT(_expr.iffalse != 0);
- Q_ASSERT(_expr.iftrue != 0);
-
- IR::BasicBlock *iftrue = _function->newBasicBlock();
- condition(ast->left, iftrue, _expr.iffalse);
-
- _block = iftrue;
- condition(ast->right, _expr.iftrue, _expr.iffalse);
- } else {
- IR::BasicBlock *iftrue = _function->newBasicBlock();
- IR::BasicBlock *iffalse = _function->newBasicBlock();
- IR::BasicBlock *endif = _function->newBasicBlock();
-
- ExprResult left = expression(ast->left);
- IR::Temp *cond = _block->TEMP(IR::BoolType);
- _block->MOVE(cond, left);
- _block->CJUMP(cond, iftrue, iffalse);
-
- IR::Temp *r = _block->TEMP(IR::InvalidType);
-
- _block = iffalse;
- _block->MOVE(r, cond);
- _block->JUMP(endif);
-
- _block = iftrue;
- ExprResult right = expression(ast->right);
- _block->MOVE(r, right);
- _block->JUMP(endif);
-
- if (left.type() != right.type())
- discard();
-
- _block = endif;
-
- r->type = right.type();
- _expr.code = r;
- }
- } break;
-
- case QSOperator::Or: {
- IR::BasicBlock *iftrue = _function->newBasicBlock();
- IR::BasicBlock *endif = _function->newBasicBlock();
-
- ExprResult left = expression(ast->left);
- IR::Temp *r = _block->TEMP(left.type());
- _block->MOVE(r, left);
-
- IR::Expr *cond = r;
- if (r->type != IR::BoolType) {
- cond = _block->TEMP(IR::BoolType);
- _block->MOVE(cond, r);
- }
-
- _block->CJUMP(_block->UNOP(IR::OpNot, cond), iftrue, endif);
-
- _block = iftrue;
- ExprResult right = expression(ast->right);
- _block->MOVE(r, right);
- _block->JUMP(endif);
-
- if (left.type() != right.type())
- discard();
-
- _expr.code = r;
-
- _block = endif;
- } break;
-
- case QSOperator::Lt:
- case QSOperator::Gt:
- case QSOperator::Le:
- case QSOperator::Ge: {
- ExprResult left = expression(ast->left);
- ExprResult right = expression(ast->right);
- if (left.type() == IR::StringType && right.type() == IR::StringType) {
- binop(ast, left, right);
- } else if (left.isValid() && right.isValid()) {
- implicitCvt(left, IR::NumberType);
- implicitCvt(right, IR::NumberType);
- binop(ast, left, right);
- }
- } break;
-
- case QSOperator::NotEqual:
- case QSOperator::Equal: {
- ExprResult left = expression(ast->left);
- ExprResult right = expression(ast->right);
- if ((left.type() == IR::NullType || left.type() == IR::UndefinedType) &&
- (right.type() == IR::NullType || right.type() == IR::UndefinedType)) {
- const bool isEq = ast->op == QSOperator::Equal;
- if (_expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
- _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0);
- }
- } else if ((left.type() == IR::StringType && right.type() >= IR::FirstNumberType) ||
- (left.type() >= IR::FirstNumberType && right.type() == IR::StringType)) {
- implicitCvt(left, IR::NumberType);
- implicitCvt(right, IR::NumberType);
- binop(ast, left, right);
- } else if (left.isValid() && right.isValid()) {
- binop(ast, left, right);
- }
- } break;
-
- case QSOperator::StrictEqual:
- case QSOperator::StrictNotEqual: {
- ExprResult left = expression(ast->left);
- ExprResult right = expression(ast->right);
- if (left.type() == right.type()) {
- binop(ast, left, right);
- } else if (left.type() > IR::BoolType && right.type() > IR::BoolType) {
- // left and right have numeric type (int or real)
- binop(ast, left, right);
- } else if ((left.type() == IR::ObjectType && right.type() == IR::NullType) ||
- (right.type() == IR::ObjectType && left.type() == IR::NullType)) {
- // comparing a qobject with null
- binop(ast, left, right);
- } else if (left.isValid() && right.isValid()) {
- // left and right have different types
- const bool isEq = ast->op == QSOperator::StrictEqual;
- if (_expr.hint == ExprResult::cx) {
- _expr.format = ExprResult::cx;
- _block->JUMP(isEq ? _expr.iffalse : _expr.iftrue);
- } else {
- _expr.code = _block->CONST(IR::BoolType, isEq ? 0 : 1);
- }
- }
- } break;
-
- case QSOperator::BitAnd:
- case QSOperator::BitOr:
- case QSOperator::BitXor:
- case QSOperator::LShift:
- case QSOperator::RShift:
- case QSOperator::URShift: {
- ExprResult left = expression(ast->left);
- if (left.is(IR::InvalidType))
- return false;
-
- ExprResult right = expression(ast->right);
- if (right.is(IR::InvalidType))
- return false;
-
- implicitCvt(left, IR::IntType);
- implicitCvt(right, IR::IntType);
-
- IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr.code, code);
-
- } break;
-
- case QSOperator::Add: {
- ExprResult left = expression(ast->left);
- if (left.is(IR::InvalidType))
- return false;
-
- ExprResult right = expression(ast->right);
- if (right.is(IR::InvalidType))
- return false;
-
- if (left.isPrimitive() && right.isPrimitive()) {
- if (left.type() == IR::StringType || right.type() == IR::StringType) {
- implicitCvt(left, IR::StringType);
- implicitCvt(right, IR::StringType);
- }
- binop(ast, left, right);
- }
- } break;
-
- case QSOperator::Div:
- case QSOperator::Mod:
- case QSOperator::Mul:
- case QSOperator::Sub: {
- ExprResult left = expression(ast->left);
- if (left.is(IR::InvalidType))
- return false;
-
- ExprResult right = expression(ast->right);
- if (right.is(IR::InvalidType))
- return false;
-
- IR::Type t = maxType(left.type(), right.type());
- if (t >= IR::FirstNumberType) {
- implicitCvt(left, IR::NumberType);
- implicitCvt(right, IR::NumberType);
-
- IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right);
- _expr.code = _block->TEMP(code->type);
- _block->MOVE(_expr.code, code);
- }
- } break;
-
- case QSOperator::In:
- case QSOperator::InstanceOf:
- case QSOperator::Assign:
- 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:
- // yup, we don't do those.
- break;
- } // switch
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ConditionalExpression *ast)
-{
- IR::BasicBlock *iftrue = _function->newBasicBlock();
- IR::BasicBlock *iffalse = _function->newBasicBlock();
- IR::BasicBlock *endif = _function->newBasicBlock();
-
- condition(ast->expression, iftrue, iffalse);
-
- IR::Temp *r = _block->TEMP(IR::InvalidType);
-
- qSwap(_block, iftrue);
- ExprResult ok = expression(ast->ok);
- _block->MOVE(r, ok);
- _block->JUMP(endif);
- qSwap(_block, iftrue);
-
- qSwap(_block, iffalse);
- ExprResult ko = expression(ast->ko);
- _block->MOVE(r, ko);
- _block->JUMP(endif);
- qSwap(_block, iffalse);
-
- r->type = maxType(ok.type(), ko.type());
- _expr.code = r;
-
- _block = endif;
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::Expression *ast)
-{
- _block->EXP(expression(ast->left));
- _expr = expression(ast->right);
-
- return false;
-}
-
-
-// statements
-bool QV4IRBuilder::visit(AST::Block *ast)
-{
- if (ast->statements && ! ast->statements->next) {
- // we have one and only one statement
- accept(ast->statements->statement);
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::StatementList *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::VariableStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::VariableDeclarationList *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::VariableDeclaration *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::EmptyStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ExpressionStatement *ast)
-{
- if (ast->expression) {
- // return the value of this expression
- return true;
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::IfStatement *ast)
-{
- if (! ast->ko) {
- // This is an if statement without an else branch.
- discard();
- } else {
- IR::BasicBlock *iftrue = _function->newBasicBlock();
- IR::BasicBlock *iffalse = _function->newBasicBlock();
- IR::BasicBlock *endif = _function->newBasicBlock();
-
- condition(ast->expression, iftrue, iffalse);
-
- IR::Temp *r = _block->TEMP(IR::InvalidType);
-
- qSwap(_block, iftrue);
- ExprResult ok = statement(ast->ok);
- _block->MOVE(r, ok);
- _block->JUMP(endif);
- qSwap(_block, iftrue);
-
- qSwap(_block, iffalse);
- ExprResult ko = statement(ast->ko);
- _block->MOVE(r, ko);
- _block->JUMP(endif);
- qSwap(_block, iffalse);
-
- r->type = maxType(ok.type(), ko.type());
- _expr.code = r;
-
- _block = endif;
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::DoWhileStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::WhileStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ForStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::LocalForStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ForEachStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::LocalForEachStatement *)
-{
- discard();
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ContinueStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::BreakStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ReturnStatement *ast)
-{
- if (ast->expression) {
- // return the value of the expression
- return true;
- }
-
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::WithStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::SwitchStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::CaseBlock *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::CaseClauses *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::CaseClause *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::DefaultClause *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::LabelledStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::ThrowStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::TryStatement *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::Catch *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::Finally *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FunctionDeclaration *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FunctionExpression *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FormalParameterList *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::FunctionBody *)
-{
- return false;
-}
-
-bool QV4IRBuilder::visit(AST::DebuggerStatement *)
-{
- return false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h
deleted file mode 100644
index 86baae463d..0000000000
--- a/src/qml/qml/v4/qv4irbuilder_p.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4IRBUILDER_P_H
-#define QV4IRBUILDER_P_H
-
-#include <QtCore/qglobal.h>
-
-#include "qv4ir_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QV4IRBuilder : public QQmlJS::AST::Visitor
-{
-public:
- QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *);
-
- bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *, bool *invalidatable);
-
-protected:
- struct ExprResult {
- enum Format {
- ex, // expression
- cx // condition
- };
-
- QQmlJS::IR::Expr *code;
- QQmlJS::IR::BasicBlock *iftrue;
- QQmlJS::IR::BasicBlock *iffalse;
- Format hint; // requested format
- Format format; // instruction format
-
- ExprResult(QQmlJS::IR::Expr *expr = 0)
- : code(expr), iftrue(0), iffalse(0), hint(ex), format(ex) {}
-
- ExprResult(QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse)
- : code(0), iftrue(iftrue), iffalse(iffalse), hint(cx), format(ex) {}
-
- inline QQmlJS::IR::Type type() const { return code ? code->type : QQmlJS::IR::InvalidType; }
-
- inline QQmlJS::IR::Expr *get() const { return code; }
- inline operator QQmlJS::IR::Expr *() const { return get(); }
- inline QQmlJS::IR::Expr *operator->() const { return get(); }
- inline bool isValid() const { return code ? code->type != QQmlJS::IR::InvalidType : false; }
- inline bool is(QQmlJS::IR::Type t) const { return type() == t; }
- inline bool isNot(QQmlJS::IR::Type t) const { return type() != t; }
-
- bool isPrimitive() const {
- switch (type()) {
- case QQmlJS::IR::UndefinedType: // ### TODO
- case QQmlJS::IR::NullType: // ### TODO
- case QQmlJS::IR::UrlType: // ### TODO
- return false;
-
- case QQmlJS::IR::StringType:
- case QQmlJS::IR::BoolType:
- case QQmlJS::IR::IntType:
- case QQmlJS::IR::FloatType:
- case QQmlJS::IR::NumberType:
- return true;
-
- default:
- return false;
- } // switch
- }
- };
-
- inline void accept(QQmlJS::AST::Node *ast) { QQmlJS::AST::Node::accept(ast, this); }
-
- ExprResult expression(QQmlJS::AST::ExpressionNode *ast);
- ExprResult statement(QQmlJS::AST::Statement *ast);
- void sourceElement(QQmlJS::AST::SourceElement *ast);
- void condition(QQmlJS::AST::ExpressionNode *ast, QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse);
- void binop(QQmlJS::AST::BinaryExpression *ast, ExprResult left, ExprResult right);
-
- void implicitCvt(ExprResult &expr, QQmlJS::IR::Type type);
-
- virtual bool preVisit(QQmlJS::AST::Node *ast);
-
- // QML
- virtual bool visit(QQmlJS::AST::UiProgram *ast);
- virtual bool visit(QQmlJS::AST::UiImportList *ast);
- virtual bool visit(QQmlJS::AST::UiImport *ast);
- virtual bool visit(QQmlJS::AST::UiPublicMember *ast);
- virtual bool visit(QQmlJS::AST::UiSourceElement *ast);
- virtual bool visit(QQmlJS::AST::UiObjectDefinition *ast);
- virtual bool visit(QQmlJS::AST::UiObjectInitializer *ast);
- virtual bool visit(QQmlJS::AST::UiObjectBinding *ast);
- virtual bool visit(QQmlJS::AST::UiScriptBinding *ast);
- virtual bool visit(QQmlJS::AST::UiArrayBinding *ast);
- virtual bool visit(QQmlJS::AST::UiObjectMemberList *ast);
- virtual bool visit(QQmlJS::AST::UiArrayMemberList *ast);
- virtual bool visit(QQmlJS::AST::UiQualifiedId *ast);
-
- // JS
- virtual bool visit(QQmlJS::AST::Program *ast);
- virtual bool visit(QQmlJS::AST::SourceElements *ast);
- virtual bool visit(QQmlJS::AST::FunctionSourceElement *ast);
- virtual bool visit(QQmlJS::AST::StatementSourceElement *ast);
-
- // object literals
- virtual bool visit(QQmlJS::AST::PropertyAssignmentList *ast);
- virtual bool visit(QQmlJS::AST::PropertyNameAndValue *ast);
- virtual bool visit(QQmlJS::AST::PropertyGetterSetter *ast);
- virtual bool visit(QQmlJS::AST::IdentifierPropertyName *ast);
- virtual bool visit(QQmlJS::AST::StringLiteralPropertyName *ast);
- virtual bool visit(QQmlJS::AST::NumericLiteralPropertyName *ast);
-
- // array literals
- virtual bool visit(QQmlJS::AST::ElementList *ast);
- virtual bool visit(QQmlJS::AST::Elision *ast);
-
- // function calls
- virtual bool visit(QQmlJS::AST::ArgumentList *ast);
-
- // expressions
- virtual bool visit(QQmlJS::AST::ObjectLiteral *ast);
- virtual bool visit(QQmlJS::AST::ArrayLiteral *ast);
- virtual bool visit(QQmlJS::AST::ThisExpression *ast);
- virtual bool visit(QQmlJS::AST::IdentifierExpression *ast);
- virtual bool visit(QQmlJS::AST::NullExpression *ast);
- virtual bool visit(QQmlJS::AST::TrueLiteral *ast);
- virtual bool visit(QQmlJS::AST::FalseLiteral *ast);
- virtual bool visit(QQmlJS::AST::StringLiteral *ast);
- virtual bool visit(QQmlJS::AST::NumericLiteral *ast);
- virtual bool visit(QQmlJS::AST::RegExpLiteral *ast);
- virtual bool visit(QQmlJS::AST::NestedExpression *ast);
- virtual bool visit(QQmlJS::AST::ArrayMemberExpression *ast);
- virtual bool visit(QQmlJS::AST::FieldMemberExpression *ast);
- virtual bool visit(QQmlJS::AST::NewMemberExpression *ast);
- virtual bool visit(QQmlJS::AST::NewExpression *ast);
- virtual bool visit(QQmlJS::AST::CallExpression *ast);
- virtual bool visit(QQmlJS::AST::PostIncrementExpression *ast);
- virtual bool visit(QQmlJS::AST::PostDecrementExpression *ast);
- virtual bool visit(QQmlJS::AST::DeleteExpression *ast);
- virtual bool visit(QQmlJS::AST::VoidExpression *ast);
- virtual bool visit(QQmlJS::AST::TypeOfExpression *ast);
- virtual bool visit(QQmlJS::AST::PreIncrementExpression *ast);
- virtual bool visit(QQmlJS::AST::PreDecrementExpression *ast);
- virtual bool visit(QQmlJS::AST::UnaryPlusExpression *ast);
- virtual bool visit(QQmlJS::AST::UnaryMinusExpression *ast);
- virtual bool visit(QQmlJS::AST::TildeExpression *ast);
- virtual bool visit(QQmlJS::AST::NotExpression *ast);
- virtual bool visit(QQmlJS::AST::BinaryExpression *ast);
- virtual bool visit(QQmlJS::AST::ConditionalExpression *ast);
- virtual bool visit(QQmlJS::AST::Expression *ast);
-
- // statements
- virtual bool visit(QQmlJS::AST::Block *ast);
- virtual bool visit(QQmlJS::AST::StatementList *ast);
- virtual bool visit(QQmlJS::AST::VariableStatement *ast);
- virtual bool visit(QQmlJS::AST::VariableDeclarationList *ast);
- virtual bool visit(QQmlJS::AST::VariableDeclaration *ast);
- virtual bool visit(QQmlJS::AST::EmptyStatement *ast);
- virtual bool visit(QQmlJS::AST::ExpressionStatement *ast);
- virtual bool visit(QQmlJS::AST::IfStatement *ast);
- virtual bool visit(QQmlJS::AST::DoWhileStatement *ast);
- virtual bool visit(QQmlJS::AST::WhileStatement *ast);
- virtual bool visit(QQmlJS::AST::ForStatement *ast);
- virtual bool visit(QQmlJS::AST::LocalForStatement *ast);
- virtual bool visit(QQmlJS::AST::ForEachStatement *ast);
- virtual bool visit(QQmlJS::AST::LocalForEachStatement *ast);
- virtual bool visit(QQmlJS::AST::ContinueStatement *ast);
- virtual bool visit(QQmlJS::AST::BreakStatement *ast);
- virtual bool visit(QQmlJS::AST::ReturnStatement *ast);
- virtual bool visit(QQmlJS::AST::WithStatement *ast);
- virtual bool visit(QQmlJS::AST::SwitchStatement *ast);
- virtual bool visit(QQmlJS::AST::CaseBlock *ast);
- virtual bool visit(QQmlJS::AST::CaseClauses *ast);
- virtual bool visit(QQmlJS::AST::CaseClause *ast);
- virtual bool visit(QQmlJS::AST::DefaultClause *ast);
- virtual bool visit(QQmlJS::AST::LabelledStatement *ast);
- virtual bool visit(QQmlJS::AST::ThrowStatement *ast);
- virtual bool visit(QQmlJS::AST::TryStatement *ast);
- virtual bool visit(QQmlJS::AST::Catch *ast);
- virtual bool visit(QQmlJS::AST::Finally *ast);
- virtual bool visit(QQmlJS::AST::FunctionDeclaration *ast);
- virtual bool visit(QQmlJS::AST::FunctionExpression *ast);
- virtual bool visit(QQmlJS::AST::FormalParameterList *ast);
- virtual bool visit(QQmlJS::AST::FunctionBody *ast);
- virtual bool visit(QQmlJS::AST::DebuggerStatement *ast);
-
-private:
- bool buildName(QList<QStringRef> &name, QQmlJS::AST::Node *node,
- QList<QQmlJS::AST::ExpressionNode *> *nodes);
- void discard();
-
- const QV4Compiler::Expression *m_expression;
- QQmlEnginePrivate *m_engine;
-
- QQmlJS::IR::Function *_function;
- QQmlJS::IR::BasicBlock *_block;
- bool _discard;
- bool _invalidatable;
-
- ExprResult _expr;
-};
-
-QT_END_NAMESPACE
-
-#endif // QV4IRBUILDER_P_H
diff --git a/src/qml/qml/v4/qv4isel_llvm.cpp b/src/qml/qml/v4/qv4isel_llvm.cpp
new file mode 100644
index 0000000000..70378a4d8a
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_llvm.cpp
@@ -0,0 +1,1388 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-parameter"
+#endif // __clang__
+
+#include <llvm/Analysis/Passes.h>
+#include <llvm/Analysis/Verifier.h>
+#include <llvm/Assembly/PrintModulePass.h>
+#include <llvm/Bitcode/ReaderWriter.h>
+#include <llvm/ExecutionEngine/ExecutionEngine.h>
+#include <llvm/ExecutionEngine/JIT.h>
+#include <llvm/ExecutionEngine/JITMemoryManager.h>
+#include <llvm/Support/FormattedStream.h>
+#include <llvm/Support/Host.h>
+#include <llvm/Support/MemoryBuffer.h>
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/system_error.h>
+#include <llvm/Support/TargetRegistry.h>
+#include <llvm/Support/TargetSelect.h>
+#include <llvm/Target/TargetMachine.h>
+#include <llvm/Transforms/Scalar.h>
+#include <llvm/Transforms/IPO.h>
+#include <llvm/Linker.h>
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif // __clang__
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QLibrary>
+#include <QtCore/QStringList>
+#include <QtCore/QTextStream>
+#include <cstdio>
+#include <iostream>
+
+// These includes have to come last, because WTF/Platform.h defines some macros
+// with very unfriendly names that collide with class fields in LLVM.
+#include "qv4isel_llvm_p.h"
+#include "qv4_llvm_p.h"
+#include "qv4jsir_p.h"
+#include "qv4string_p.h"
+#include "qv4global_p.h"
+
+namespace QQmlJS {
+
+Q_QML_EXPORT int compileWithLLVM(V4IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *))
+{
+ Q_ASSERT(module);
+ Q_ASSERT(exec || outputType != LLVMOutputJit);
+
+ // TODO: should this be done here?
+ LLVMInitializeX86TargetInfo();
+ LLVMInitializeX86Target();
+ LLVMInitializeX86AsmPrinter();
+ LLVMInitializeX86AsmParser();
+ LLVMInitializeX86Disassembler();
+ LLVMInitializeX86TargetMC();
+
+ //----
+
+ llvm::InitializeNativeTarget();
+ LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext());
+
+ const QString moduleName = QFileInfo(fileName).fileName();
+ llvm::StringRef moduleId(moduleName.toUtf8().constData());
+ llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext());
+
+ if (outputType == LLVMOutputJit) {
+ // The execution engine takes ownership of the model. No need to delete it anymore.
+ std::string errStr;
+ llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule)
+// .setUseMCJIT(true)
+ .setErrorStr(&errStr).create();
+ if (!execEngine) {
+ std::cerr << "Could not create LLVM JIT: " << errStr << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ llvm::FunctionPassManager functionPassManager(llvmModule);
+ // Set up the optimizer pipeline. Start with registering info about how the
+ // target lays out data structures.
+ functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout()));
+ // Promote allocas to registers.
+ functionPassManager.add(llvm::createPromoteMemoryToRegisterPass());
+ // Provide basic AliasAnalysis support for GVN.
+ functionPassManager.add(llvm::createBasicAliasAnalysisPass());
+ // Do simple "peephole" optimizations and bit-twiddling optzns.
+ functionPassManager.add(llvm::createInstructionCombiningPass());
+ // Reassociate expressions.
+ functionPassManager.add(llvm::createReassociatePass());
+ // Eliminate Common SubExpressions.
+ functionPassManager.add(llvm::createGVNPass());
+ // Simplify the control flow graph (deleting unreachable blocks, etc).
+ functionPassManager.add(llvm::createCFGSimplificationPass());
+
+ functionPassManager.doInitialization();
+
+ llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager);
+
+ llvm::Function *entryPoint = llvmModule->getFunction("%entry");
+ Q_ASSERT(entryPoint);
+ void *funcPtr = execEngine->getPointerToFunction(entryPoint);
+ return exec(funcPtr);
+ } else {
+ llvm::FunctionPassManager functionPassManager(llvmModule);
+ // Set up the optimizer pipeline.
+ // Promote allocas to registers.
+ functionPassManager.add(llvm::createPromoteMemoryToRegisterPass());
+ // Provide basic AliasAnalysis support for GVN.
+ functionPassManager.add(llvm::createBasicAliasAnalysisPass());
+ // Do simple "peephole" optimizations and bit-twiddling optzns.
+ functionPassManager.add(llvm::createInstructionCombiningPass());
+ // Reassociate expressions.
+ functionPassManager.add(llvm::createReassociatePass());
+ // Eliminate Common SubExpressions.
+ functionPassManager.add(llvm::createGVNPass());
+ // Simplify the control flow graph (deleting unreachable blocks, etc).
+ functionPassManager.add(llvm::createCFGSimplificationPass());
+
+ functionPassManager.doInitialization();
+
+ llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager);
+
+ // TODO: if output type is .ll, print the module to file
+
+ const std::string triple = llvm::sys::getDefaultTargetTriple();
+
+ std::string err;
+ const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err);
+ if (! err.empty()) {
+ std::cerr << err << ", triple: " << triple << std::endl;
+ assert(!"cannot create target for the host triple");
+ }
+
+ std::string cpu;
+ std::string features;
+ llvm::TargetOptions options;
+ llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_);
+ assert(targetMachine);
+
+ llvm::TargetMachine::CodeGenFileType ft;
+ QString ofName;
+
+ if (outputType == LLVMOutputObject) {
+ ft = llvm::TargetMachine::CGFT_ObjectFile;
+ ofName = fileName + QLatin1String(".o");
+ } else if (outputType == LLVMOutputAssembler) {
+ ft = llvm::TargetMachine::CGFT_AssemblyFile;
+ ofName = fileName + QLatin1String(".s");
+ } else {
+ // ft is not used.
+ ofName = fileName + QLatin1String(".ll");
+ }
+
+ llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary);
+ llvm::formatted_raw_ostream destf(dest);
+ if (!err.empty()) {
+ std::cerr << err << std::endl;
+ delete llvmModule;
+ }
+
+ llvm::PassManager globalPassManager;
+ globalPassManager.add(llvm::createScalarReplAggregatesPass());
+ globalPassManager.add(llvm::createInstructionCombiningPass());
+ globalPassManager.add(llvm::createGlobalOptimizerPass());
+ globalPassManager.add(llvm::createFunctionInliningPass(25));
+// globalPassManager.add(llvm::createFunctionInliningPass(125));
+
+ if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) {
+ if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) {
+ std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl;
+ } else {
+ globalPassManager.run(*llvmModule);
+
+ destf.flush();
+ dest.flush();
+ }
+ } else { // .ll
+ globalPassManager.run(*llvmModule);
+ llvmModule->print(destf, 0);
+
+ destf.flush();
+ dest.flush();
+ }
+
+ delete llvmModule;
+ return EXIT_SUCCESS;
+ }
+}
+
+} // QQmlJS
+
+using namespace QQmlJS;
+using namespace QQmlJS::LLVM;
+
+namespace {
+QTextStream qerr(stderr, QIODevice::WriteOnly);
+}
+
+InstructionSelection::InstructionSelection(llvm::LLVMContext &context)
+ : llvm::IRBuilder<>(context)
+ , _llvmModule(0)
+ , _llvmFunction(0)
+ , _llvmValue(0)
+ , _numberTy(0)
+ , _valueTy(0)
+ , _contextPtrTy(0)
+ , _stringPtrTy(0)
+ , _functionTy(0)
+ , _allocaInsertPoint(0)
+ , _function(0)
+ , _block(0)
+ , _fpm(0)
+{
+}
+
+void InstructionSelection::buildLLVMModule(V4IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm)
+{
+ qSwap(_llvmModule, llvmModule);
+ qSwap(_fpm, fpm);
+
+ _numberTy = getDoubleTy();
+
+ std::string err;
+
+ llvm::OwningPtr<llvm::MemoryBuffer> buffer;
+ qDebug()<<"llvm runtime:"<<LLVM_RUNTIME;
+ llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef(LLVM_RUNTIME), buffer);
+ if (ec) {
+ qWarning() << ec.message().c_str();
+ assert(!"cannot load QML/JS LLVM runtime, you can generate the runtime with the command `make llvm_runtime'");
+ }
+
+ llvm::Module *llvmRuntime = llvm::getLazyBitcodeModule(buffer.get(), getContext(), &err);
+ if (! err.empty()) {
+ qWarning() << err.c_str();
+ assert(!"cannot load QML/JS LLVM runtime");
+ }
+
+ err.clear();
+ llvm::Linker::LinkModules(_llvmModule, llvmRuntime, llvm::Linker::DestroySource, &err);
+ if (! err.empty()) {
+ qWarning() << err.c_str();
+ assert(!"cannot link the QML/JS LLVM runtime");
+ }
+
+ _valueTy = _llvmModule->getTypeByName("struct.QV4::Value");
+ _contextPtrTy = _llvmModule->getTypeByName("struct.QV4::ExecutionContext")->getPointerTo();
+ _stringPtrTy = _llvmModule->getTypeByName("struct.QV4::String")->getPointerTo();
+
+ {
+ llvm::Type *args[] = { _contextPtrTy };
+ _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false);
+ }
+
+
+ foreach (V4IR::Function *function, module->functions)
+ (void) compileLLVMFunction(function);
+ qSwap(_fpm, fpm);
+ qSwap(_llvmModule, llvmModule);
+}
+
+void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg, int line)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinCreateExceptionHandler(V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinFinishTry()
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPopScope()
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
+{
+ llvm::ConstantInt *isDeletable = getInt1(deletable != 0);
+ llvm::Value *varName = getIdentifier(name);
+ CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"),
+ _llvmFunction->arg_begin(), isDeletable, varName);
+}
+
+void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructActivationProperty(V4IR::Name *func,
+ V4IR::ExprList *args,
+ V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadThisObject(V4IR::Temp *temp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadConst(V4IR::Const *con, V4IR::Temp *temp)
+{
+ llvm::Value *target = getLLVMTemp(temp);
+ llvm::Value *source = CreateLoad(createValue(con));
+ CreateStore(source, target);
+}
+
+void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName)
+{
+ llvm::Value *name = getIdentifier(targetName);
+ llvm::Value *src = toValuePtr(source);
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"),
+ _llvmFunction->arg_begin(), name, src);
+}
+
+void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target)
+{
+ V4IR::Function *f = closure->value;
+ QString name;
+ if (f->name)
+ name = *f->name;
+
+ llvm::Value *args[] = {
+ _llvmFunction->arg_begin(),
+ getLLVMTemp(target),
+ getIdentifier(name),
+ getInt1(f->hasDirectEval),
+ getInt1(f->usesArgumentsObject),
+ getInt1(f->isStrict),
+ getInt1(!f->nestedFunctions.isEmpty()),
+ genStringList(f->formals, "formals", "formal"),
+ getInt32(f->formals.size()),
+ genStringList(f->locals, "locals", "local"),
+ getInt32(f->locals.size())
+ };
+ llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure");
+ CreateCall(callee, args);
+}
+
+void InstructionSelection::getProperty(V4IR::Temp *sourceBase, const QString &sourceName, V4IR::Temp *target)
+{
+ llvm::Value *base = getLLVMTempReference(sourceBase);
+ llvm::Value *name = getIdentifier(sourceName);
+ llvm::Value *t = getLLVMTemp(target);
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"),
+ _llvmFunction->arg_begin(), t, base, name);
+}
+
+void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ llvm::Value *base = getLLVMTempReference(targetBase);
+ llvm::Value *name = getIdentifier(targetName);
+ llvm::Value *src = toValuePtr(source);
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"),
+ _llvmFunction->arg_begin(), base, name, src);
+}
+
+void InstructionSelection::getElement(V4IR::Temp *sourceBase, V4IR::Temp *sourceIndex, V4IR::Temp *target)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+
+ llvm::Value *base = getLLVMTempReference(sourceBase);
+ llvm::Value *index = getLLVMTempReference(sourceIndex);
+ llvm::Value *t = getLLVMTemp(target);
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"),
+ _llvmFunction->arg_begin(), t, base, index);
+}
+
+void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex)
+{
+ llvm::Value *base = getLLVMTempReference(targetBase);
+ llvm::Value *index = getLLVMTempReference(targetIndex);
+ llvm::Value *src = toValuePtr(source);
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"),
+ _llvmFunction->arg_begin(), base, index, src);
+}
+
+void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ llvm::Value *t = getLLVMTemp(targetTemp);
+ llvm::Value *s = getLLVMTemp(sourceTemp);
+ CreateStore(s, t);
+}
+
+void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpNot: opName = "__qmljs_not"; break;
+ case V4IR::OpUMinus: opName = "__qmljs_uminus"; break;
+ case V4IR::OpUPlus: opName = "__qmljs_uplus"; break;
+ case V4IR::OpCompl: opName = "__qmljs_compl"; break;
+ case V4IR::OpIncrement: opName = "__qmljs_increment"; break;
+ case V4IR::OpDecrement: opName = "__qmljs_decrement"; break;
+ default: assert(!"unreachable"); break;
+ }
+
+ if (opName) {
+ llvm::Value *t = getLLVMTemp(targetTemp);
+ llvm::Value *s = getLLVMTemp(sourceTemp);
+ CreateCall3(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), t, s);
+ }
+}
+
+void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break;
+ case V4IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break;
+ case V4IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break;
+ case V4IR::OpAdd: opName = "__qmljs_llvm_add"; break;
+ case V4IR::OpSub: opName = "__qmljs_llvm_sub"; break;
+ case V4IR::OpMul: opName = "__qmljs_llvm_mul"; break;
+ case V4IR::OpDiv: opName = "__qmljs_llvm_div"; break;
+ case V4IR::OpMod: opName = "__qmljs_llvm_mod"; break;
+ case V4IR::OpLShift: opName = "__qmljs_llvm_shl"; break;
+ case V4IR::OpRShift: opName = "__qmljs_llvm_shr"; break;
+ case V4IR::OpURShift: opName = "__qmljs_llvm_ushr"; break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (opName) {
+ llvm::Value *t = getLLVMTemp(target);
+ llvm::Value *s1 = toValuePtr(leftSource);
+ llvm::Value *s2 = toValuePtr(rightSource);
+ CreateCall4(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), t, s1, s2);
+ return;
+ }
+}
+
+void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break;
+ case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break;
+ case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break;
+ case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break;
+ case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break;
+ case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break;
+ case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break;
+ case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break;
+ case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break;
+ case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break;
+ case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (opName) {
+ llvm::Value *dst = getIdentifier(targetName);
+ llvm::Value *src = toValuePtr(rightSource);
+ CreateCall3(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), dst, src);
+ return;
+ }
+}
+
+void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break;
+ case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break;
+ case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break;
+ case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break;
+ case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break;
+ case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break;
+ case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break;
+ case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break;
+ case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break;
+ case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break;
+ case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (opName) {
+ llvm::Value *base = getLLVMTemp(targetBaseTemp);
+ llvm::Value *index = getLLVMTemp(targetIndexTemp);
+ llvm::Value *value = toValuePtr(source);
+ CreateCall4(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), base, index, value);
+ }
+}
+
+void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break;
+ case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break;
+ case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break;
+ case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break;
+ case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break;
+ case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break;
+ case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break;
+ case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break;
+ case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break;
+ case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break;
+ case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (opName) {
+ llvm::Value *base = getLLVMTemp(targetBase);
+ llvm::Value *member = getIdentifier(targetName);
+ llvm::Value *value = toValuePtr(source);
+ CreateCall4(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), value, base, member);
+ }
+}
+
+llvm::Function *InstructionSelection::getLLVMFunction(V4IR::Function *function)
+{
+ llvm::Function *&f = _functionMap[function];
+ if (! f) {
+ QString name = QStringLiteral("__qmljs_native_");
+ if (function->name) {
+ if (*function->name == QStringLiteral("%entry"))
+ name = *function->name;
+ else
+ name += *function->name;
+ }
+ f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal
+ qPrintable(name), _llvmModule);
+ }
+ return f;
+}
+
+llvm::Function *InstructionSelection::compileLLVMFunction(V4IR::Function *function)
+{
+ llvm::Function *llvmFunction = getLLVMFunction(function);
+
+ QHash<V4IR::BasicBlock *, llvm::BasicBlock *> blockMap;
+ QVector<llvm::Value *> tempMap;
+
+ qSwap(_llvmFunction, llvmFunction);
+ qSwap(_function, function);
+ qSwap(_tempMap, tempMap);
+ qSwap(_blockMap, blockMap);
+
+ // create the LLVM blocks
+ foreach (V4IR::BasicBlock *block, _function->basicBlocks)
+ (void) getLLVMBasicBlock(block);
+
+ // entry block
+ SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first()));
+
+ llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()),
+ getInt32Ty(), "", GetInsertBlock());
+ qSwap(_allocaInsertPoint, allocaInsertPoint);
+
+ for (int i = 0; i < _function->tempCount; ++i) {
+ llvm::AllocaInst *t = newLLVMTemp(_valueTy);
+ _tempMap.append(t);
+ }
+
+ foreach (llvm::Value *t, _tempMap) {
+ CreateStore(llvm::Constant::getNullValue(_valueTy), t);
+ }
+
+// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"),
+// _llvmFunction->arg_begin());
+
+ foreach (V4IR::BasicBlock *block, _function->basicBlocks) {
+ qSwap(_block, block);
+ SetInsertPoint(getLLVMBasicBlock(_block));
+ foreach (V4IR::Stmt *s, _block->statements)
+ s->accept(this);
+ qSwap(_block, block);
+ }
+
+ qSwap(_allocaInsertPoint, allocaInsertPoint);
+
+ allocaInsertPoint->eraseFromParent();
+
+ qSwap(_blockMap, blockMap);
+ qSwap(_tempMap, tempMap);
+ qSwap(_function, function);
+ qSwap(_llvmFunction, llvmFunction);
+
+ // Validate the generated code, checking for consistency.
+ llvm::verifyFunction(*llvmFunction);
+ // Optimize the function.
+ if (_fpm)
+ _fpm->run(*llvmFunction);
+
+ return llvmFunction;
+}
+
+llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(V4IR::BasicBlock *block)
+{
+ llvm::BasicBlock *&llvmBlock = _blockMap[block];
+ if (! llvmBlock)
+ llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(),
+ _llvmFunction);
+ return llvmBlock;
+}
+
+llvm::Value *InstructionSelection::getLLVMTempReference(V4IR::Expr *expr)
+{
+ if (V4IR::Temp *t = expr->asTemp())
+ return getLLVMTemp(t);
+
+ assert(!"TODO!");
+ llvm::Value *addr = newLLVMTemp(_valueTy);
+// CreateStore(getLLVMValue(expr), addr);
+ return addr;
+}
+
+llvm::Value *InstructionSelection::getLLVMCondition(V4IR::Expr *expr)
+{
+ llvm::Value *value = 0;
+ if (V4IR::Temp *t = expr->asTemp()) {
+ value = getLLVMTemp(t);
+ } else {
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+
+#if 0
+ value = getLLVMValue(expr);
+ if (! value) {
+ Q_UNIMPLEMENTED();
+ return getInt1(false);
+ }
+
+ llvm::Value *tmp = newLLVMTemp(_valueTy);
+ CreateStore(value, tmp);
+ value = tmp;
+#endif
+ }
+
+ return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"),
+ _llvmFunction->arg_begin(),
+ value);
+}
+
+llvm::Value *InstructionSelection::getLLVMTemp(V4IR::Temp *temp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+ return 0;
+#if 0
+ if (temp->idx < 0) {
+ const int index = -temp->idx -1;
+ return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"),
+ _llvmFunction->arg_begin(), getInt32(index));
+ }
+
+ return _tempMap[temp->idx];
+#endif
+}
+
+llvm::Value *InstructionSelection::getStringPtr(const QString &s)
+{
+ llvm::Value *&value = _stringMap[s];
+ if (! value) {
+ const QByteArray bytes = s.toUtf8();
+ value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size()));
+ _stringMap[s] = value;
+ }
+ return value;
+}
+
+llvm::Value *InstructionSelection::getIdentifier(const QString &s)
+{
+ llvm::Value *str = getStringPtr(s);
+ llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"),
+ _llvmFunction->arg_begin(), str);
+ return id;
+}
+
+void InstructionSelection::visitJump(V4IR::Jump *s)
+{
+ CreateBr(getLLVMBasicBlock(s->target));
+}
+
+void InstructionSelection::visitCJump(V4IR::CJump *s)
+{
+ CreateCondBr(getLLVMCondition(s->cond),
+ getLLVMBasicBlock(s->iftrue),
+ getLLVMBasicBlock(s->iffalse));
+}
+
+void InstructionSelection::visitRet(V4IR::Ret *s)
+{
+ V4IR::Temp *t = s->expr->asTemp();
+ assert(t != 0);
+ llvm::Value *result = getLLVMTemp(t);
+ llvm::Value *ctx = _llvmFunction->arg_begin();
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result);
+ CreateRetVoid();
+}
+
+void InstructionSelection::visitTry(V4IR::Try *)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+#if 0
+void InstructionSelection::visitString(V4IR::String *e)
+{
+ llvm::Value *tmp = newLLVMTemp(_valueTy);
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"),
+ _llvmFunction->arg_begin(), tmp,
+ getStringPtr(*e->value));
+ _llvmValue = CreateLoad(tmp);
+}
+#endif
+
+llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size)
+{
+ llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint);
+ return addr;
+}
+
+llvm::Value * InstructionSelection::genArguments(V4IR::ExprList *exprs, int &argc)
+{
+ llvm::Value *args = 0;
+
+ argc = 0;
+ for (V4IR::ExprList *it = exprs; it; it = it->next)
+ ++argc;
+
+ if (argc)
+ args = newLLVMTemp(_valueTy, getInt32(argc));
+ else
+ args = llvm::Constant::getNullValue(_valueTy->getPointerTo());
+
+ int i = 0;
+ for (V4IR::ExprList *it = exprs; it; it = it->next) {
+// llvm::Value *arg = getLLVMValue(it->expr);
+// CreateStore(arg, CreateConstGEP1_32(args, i++));
+ }
+
+ return args;
+}
+
+void InstructionSelection::genCallMember(V4IR::Call *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ V4IR::Member *m = e->base->asMember();
+ llvm::Value *thisObject = getLLVMTemp(m->base->asTemp());
+ llvm::Value *name = getIdentifier(*m->name);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *actuals[] = {
+ _llvmFunction->arg_begin(),
+ result,
+ thisObject,
+ name,
+ args,
+ getInt32(argc)
+ };
+
+ CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef<llvm::Value *>(actuals));
+ _llvmValue = CreateLoad(result);
+}
+
+void InstructionSelection::genConstructMember(V4IR::New *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ V4IR::Member *m = e->base->asMember();
+ llvm::Value *thisObject = getLLVMTemp(m->base->asTemp());
+ llvm::Value *name = getIdentifier(*m->name);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *actuals[] = {
+ _llvmFunction->arg_begin(),
+ result,
+ thisObject,
+ name,
+ args,
+ getInt32(argc)
+ };
+
+ CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef<llvm::Value *>(actuals));
+ _llvmValue = CreateLoad(result);
+}
+
+void InstructionSelection::genCallTemp(V4IR::Call *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ llvm::Value *func = getLLVMTempReference(e->base);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo());
+
+ llvm::Value *actuals[] = {
+ _llvmFunction->arg_begin(),
+ result,
+ thisObject,
+ func,
+ args,
+ getInt32(argc)
+ };
+
+ CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals);
+
+ _llvmValue = CreateLoad(result);
+}
+
+void InstructionSelection::genConstructTemp(V4IR::New *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ llvm::Value *func = getLLVMTempReference(e->base);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *actuals[] = {
+ _llvmFunction->arg_begin(),
+ result,
+ func,
+ args,
+ getInt32(argc)
+ };
+
+ CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals);
+
+ _llvmValue = CreateLoad(result);
+}
+
+void InstructionSelection::genCallName(V4IR::Call *e, llvm::Value *result)
+{
+ V4IR::Name *base = e->base->asName();
+
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ if (! base->id) {
+ switch (base->builtin) {
+ case V4IR::Name::builtin_invalid:
+ break;
+
+ case V4IR::Name::builtin_typeof:
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"),
+ _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case V4IR::Name::builtin_throw:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"),
+ _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr));
+ _llvmValue = llvm::UndefValue::get(_valueTy);
+ return;
+
+ case V4IR::Name::builtin_finish_try:
+ // ### FIXME.
+ return;
+
+ case V4IR::Name::builtin_foreach_iterator_object:
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"),
+ _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case V4IR::Name::builtin_foreach_next_property_name:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"),
+ result, getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case V4IR::Name::builtin_delete: {
+ if (V4IR::Subscript *subscript = e->args->expr->asSubscript()) {
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"),
+ _llvmFunction->arg_begin(),
+ result,
+ getLLVMTempReference(subscript->base),
+ getLLVMTempReference(subscript->index));
+ _llvmValue = CreateLoad(result);
+ return;
+ } else if (V4IR::Member *member = e->args->expr->asMember()) {
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"),
+ _llvmFunction->arg_begin(),
+ result,
+ getLLVMTempReference(member->base),
+ getIdentifier(*member->name));
+ _llvmValue = CreateLoad(result);
+ return;
+ } else if (V4IR::Name *name = e->args->expr->asName()) {
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"),
+ _llvmFunction->arg_begin(),
+ result,
+ getIdentifier(*name->id));
+ _llvmValue = CreateLoad(result);
+ return;
+ } else {
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"),
+ _llvmFunction->arg_begin(),
+ result,
+ getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+ }
+ } break;
+
+ default:
+ Q_UNREACHABLE();
+ }
+ } else {
+ llvm::Value *name = getIdentifier(*base->id);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"),
+ _llvmFunction->arg_begin(), result, name, args, getInt32(argc));
+
+ _llvmValue = CreateLoad(result);
+ }
+}
+
+void InstructionSelection::genConstructName(V4IR::New *e, llvm::Value *result)
+{
+ V4IR::Name *base = e->base->asName();
+
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ if (! base->id) {
+ Q_UNREACHABLE();
+ } else {
+ llvm::Value *name = getIdentifier(*base->id);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"),
+ _llvmFunction->arg_begin(), result, name, args, getInt32(argc));
+
+ _llvmValue = CreateLoad(result);
+ }
+}
+
+#if 0
+void InstructionSelection::visitCall(V4IR::Call *e)
+{
+ if (e->base->asMember()) {
+ genCallMember(e);
+ } else if (e->base->asTemp()) {
+ genCallTemp(e);
+ } else if (e->base->asName()) {
+ genCallName(e);
+ } else if (V4IR::Temp *t = e->base->asTemp()) {
+ llvm::Value *base = getLLVMTemp(t);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *result = newLLVMTemp(_valueTy);
+ CreateStore(llvm::Constant::getNullValue(_valueTy), result);
+ CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"),
+ _llvmFunction->arg_begin(), result, base, args, getInt32(argc));
+ _llvmValue = CreateLoad(result);
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+}
+#endif
+
+#if 0
+void InstructionSelection::visitNew(V4IR::New *e)
+{
+ if (e->base->asMember()) {
+ genConstructMember(e);
+ } else if (e->base->asTemp()) {
+ genConstructTemp(e);
+ } else if (e->base->asName()) {
+ genConstructName(e);
+ } else if (V4IR::Temp *t = e->base->asTemp()) {
+ llvm::Value *base = getLLVMTemp(t);
+
+ int argc = 0;
+ llvm::Value *args = genArguments(e->args, argc);
+
+ llvm::Value *result = newLLVMTemp(_valueTy);
+ CreateStore(llvm::Constant::getNullValue(_valueTy), result);
+ CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"),
+ _llvmFunction->arg_begin(), result, base, args, getInt32(argc));
+ _llvmValue = CreateLoad(result);
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+}
+#endif
+
+#if 0
+void InstructionSelection::visitSubscript(V4IR::Subscript *e)
+{
+ llvm::Value *result = newLLVMTemp(_valueTy);
+ llvm::Value *base = getLLVMTempReference(e->base);
+ llvm::Value *index = getLLVMTempReference(e->index);
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"),
+ _llvmFunction->arg_begin(), result, base, index);
+ _llvmValue = CreateLoad(result);
+}
+#endif
+
+#if 0
+void InstructionSelection::visitMember(V4IR::Member *e)
+{
+ llvm::Value *result = newLLVMTemp(_valueTy);
+ llvm::Value *base = getLLVMTempReference(e->base);
+ llvm::Value *name = getIdentifier(*e->name);
+
+ CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"),
+ _llvmFunction->arg_begin(), result, base, name);
+ _llvmValue = CreateLoad(result);
+}
+#endif
+
+llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str)
+{
+ llvm::Function *func = _llvmModule->getFunction(str);
+ if (!func) {
+ std::cerr << "Cannot find runtime function \""
+ << str.str() << "\"!" << std::endl;
+ assert(func);
+ }
+ return func;
+}
+
+llvm::Value *InstructionSelection::createValue(V4IR::Const *e)
+{
+ llvm::Value *tmp = newLLVMTemp(_valueTy);
+
+ switch (e->type) {
+ case V4IR::UndefinedType:
+ CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp);
+ break;
+
+ case V4IR::NullType:
+ CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp);
+ break;
+
+ case V4IR::BoolType:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp,
+ getInt1(e->value ? 1 : 0));
+ break;
+
+ case V4IR::NumberType:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp,
+ llvm::ConstantFP::get(_numberTy, e->value));
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ }
+
+ return tmp;
+}
+
+llvm::Value *InstructionSelection::toValuePtr(V4IR::Expr *e)
+{
+ if (V4IR::Temp *t = e->asTemp()) {
+ return getLLVMTemp(t);
+ } else if (V4IR::Const *c = e->asConst()) {
+ return createValue(c);
+ } else {
+ Q_UNREACHABLE();
+ }
+}
+
+llvm::Value *InstructionSelection::genStringList(const QList<const QString *> &strings, const char *arrayName, const char *elementName)
+{
+ llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()),
+ arrayName);
+ for (int i = 0, ei = strings.size(); i < ei; ++i) {
+ llvm::Value *el;
+ if (const QString *string = strings.at(i))
+ el = getIdentifier(*string);
+ else
+ el = llvm::Constant::getNullValue(_stringPtrTy);
+ llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName);
+ CreateStore(el, ptr);
+ }
+
+ return array;
+}
diff --git a/src/qml/qml/v4/qv4isel_llvm_p.h b/src/qml/qml/v4/qv4isel_llvm_p.h
new file mode 100644
index 0000000000..ab0bcb0e23
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_llvm_p.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ISEL_LLVM_P_H
+#define QV4ISEL_LLVM_P_H
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-parameter"
+#endif // __clang__
+
+#include <llvm/Module.h>
+#include <llvm/PassManager.h>
+#include <llvm/IRBuilder.h>
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif // __clang__
+
+#include "qv4isel_p.h"
+#include "qv4jsir_p.h"
+
+namespace QQmlJS {
+namespace LLVM {
+
+class InstructionSelection:
+ public llvm::IRBuilder<>,
+ public V4IR::InstructionSelection
+{
+public:
+ InstructionSelection(llvm::LLVMContext &context);
+
+ void buildLLVMModule(V4IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm);
+
+public: // methods from InstructionSelection:
+ virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteValue(V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinThrow(V4IR::Temp *arg, int line);
+ virtual void callBuiltinCreateExceptionHandler(V4IR::Temp *result);
+ virtual void callBuiltinFinishTry();
+ virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinPushWithScope(V4IR::Temp *arg);
+ virtual void callBuiltinPopScope();
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name);
+ virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter);
+ virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value);
+ virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void loadThisObject(V4IR::Temp *temp);
+ virtual void loadConst(V4IR::Const *con, V4IR::Temp *temp);
+ virtual void loadString(const QString &str, V4IR::Temp *targetTemp);
+ virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp);
+ virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp);
+ virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName);
+ virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target);
+ virtual void getProperty(V4IR::Temp *sourceBase, const QString &sourceName, V4IR::Temp *target);
+ virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(V4IR::Temp *sourceBase, V4IR::Temp *sourceIndex, V4IR::Temp *target);
+ virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex);
+ virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target);
+ virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName);
+ virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+
+public: // visitor methods for StmtVisitor:
+ virtual void visitJump(V4IR::Jump *);
+ virtual void visitCJump(V4IR::CJump *);
+ virtual void visitRet(V4IR::Ret *);
+ virtual void visitTry(V4IR::Try *);
+
+private:
+ llvm::Function *getRuntimeFunction(llvm::StringRef str);
+ llvm::Function *getLLVMFunction(V4IR::Function *function);
+ llvm::Function *compileLLVMFunction(V4IR::Function *function);
+ llvm::BasicBlock *getLLVMBasicBlock(V4IR::BasicBlock *block);
+ llvm::Value *getLLVMTempReference(V4IR::Expr *expr);
+ llvm::Value *getLLVMCondition(V4IR::Expr *expr);
+ llvm::Value *getLLVMTemp(V4IR::Temp *temp);
+ llvm::Value *getStringPtr(const QString &s);
+ llvm::Value *getIdentifier(const QString &s);
+ llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0);
+ llvm::Value * genArguments(V4IR::ExprList *args, int &argc);
+ void genCallTemp(V4IR::Call *e, llvm::Value *result = 0);
+ void genCallName(V4IR::Call *e, llvm::Value *result = 0);
+ void genCallMember(V4IR::Call *e, llvm::Value *result = 0);
+ void genConstructTemp(V4IR::New *e, llvm::Value *result = 0);
+ void genConstructName(V4IR::New *e, llvm::Value *result = 0);
+ void genConstructMember(V4IR::New *e, llvm::Value *result = 0);
+ llvm::Value *createValue(V4IR::Const *e);
+ llvm::Value *toValuePtr(V4IR::Expr *e);
+ llvm::Value *genStringList(const QList<const QString *> &strings,
+ const char *arrayName, const char *elementName);
+
+
+private:
+ llvm::Module *_llvmModule;
+ llvm::Function *_llvmFunction;
+ llvm::Value *_llvmValue;
+ llvm::Type *_numberTy;
+ llvm::Type *_valueTy;
+ llvm::Type *_contextPtrTy;
+ llvm::Type *_stringPtrTy;
+ llvm::FunctionType *_functionTy;
+ llvm::Instruction *_allocaInsertPoint;
+ V4IR::Function *_function;
+ V4IR::BasicBlock *_block;
+ QHash<V4IR::Function *, llvm::Function *> _functionMap;
+ QHash<V4IR::BasicBlock *, llvm::BasicBlock *> _blockMap;
+ QVector<llvm::Value *> _tempMap;
+ QHash<QString, llvm::Value *> _stringMap;
+ llvm::FunctionPassManager *_fpm;
+};
+
+} // LLVM namespace
+} // QQmlJS namespace
+
+#endif // QV4ISEL_LLVM_P_H
diff --git a/src/qml/qml/v4/qv4isel_masm.cpp b/src/qml/qml/v4/qv4isel_masm.cpp
new file mode 100644
index 0000000000..af77a7cb17
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_masm.cpp
@@ -0,0 +1,1450 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4isel_masm_p.h"
+#include "qv4runtime_p.h"
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4regexpobject_p.h"
+#include "qv4unwindhelper_p.h"
+#include "qv4lookup_p.h"
+#include "qv4function_p.h"
+#include "qv4ssa_p.h"
+#include "qv4exception_p.h"
+
+#include <assembler/LinkBuffer.h>
+#include <WTFStubs.h>
+
+#include <iostream>
+#include <cassert>
+
+#if USE(UDIS86)
+# include <udis86.h>
+#endif
+
+using namespace QQmlJS;
+using namespace QQmlJS::MASM;
+using namespace QV4;
+
+namespace {
+class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor
+{
+ int _nextFreeStackSlot;
+ QHash<V4IR::Temp, int> _stackSlotForTemp;
+
+ void renumber(V4IR::Temp *t)
+ {
+ if (t->kind != V4IR::Temp::VirtualRegister)
+ return;
+
+ int stackSlot = _stackSlotForTemp.value(*t, -1);
+ if (stackSlot == -1) {
+ stackSlot = _nextFreeStackSlot++;
+ _stackSlotForTemp[*t] = stackSlot;
+ }
+
+ t->kind = V4IR::Temp::StackSlot;
+ t->index = stackSlot;
+ }
+
+public:
+ ConvertTemps()
+ : _nextFreeStackSlot(0)
+ {}
+
+ void toStackSlots(V4IR::Function *function)
+ {
+ _stackSlotForTemp.reserve(function->tempCount);
+
+ foreach (V4IR::BasicBlock *bb, function->basicBlocks)
+ foreach (V4IR::Stmt *s, bb->statements)
+ s->accept(this);
+
+ function->tempCount = _nextFreeStackSlot;
+ }
+
+protected:
+ virtual void visitConst(V4IR::Const *) {}
+ virtual void visitString(V4IR::String *) {}
+ virtual void visitRegExp(V4IR::RegExp *) {}
+ virtual void visitName(V4IR::Name *) {}
+ virtual void visitTemp(V4IR::Temp *e) { renumber(e); }
+ virtual void visitClosure(V4IR::Closure *) {}
+ virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); }
+ virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitCall(V4IR::Call *e) {
+ e->base->accept(this);
+ for (V4IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+ virtual void visitNew(V4IR::New *e) {
+ e->base->accept(this);
+ for (V4IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+ virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(V4IR::Member *e) { e->base->accept(this); }
+ virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); }
+ virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); }
+ virtual void visitJump(V4IR::Jump *) {}
+ virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); }
+ virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); }
+ virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); }
+};
+} // anonymous namespace
+
+/* Platform/Calling convention/Architecture specific section */
+
+#if CPU(X86_64)
+static const Assembler::RegisterID calleeSavedRegisters[] = {
+ // Not used: JSC::X86Registers::rbx,
+ // Not used: JSC::X86Registers::r10,
+ JSC::X86Registers::r12, // LocalsRegister
+ // Not used: JSC::X86Registers::r13,
+ JSC::X86Registers::r14 // ContextRegister
+ // Not used: JSC::X86Registers::r15,
+};
+#endif
+
+#if CPU(X86)
+static const Assembler::RegisterID calleeSavedRegisters[] = {
+ // Not used: JSC::X86Registers::ebx,
+ JSC::X86Registers::esi, // ContextRegister
+ JSC::X86Registers::edi // LocalsRegister
+};
+#endif
+
+#if CPU(ARM)
+static const Assembler::RegisterID calleeSavedRegisters[] = {
+ // ### FIXME: remove unused registers.
+ // Keep these in reverse order and make sure to also edit the unwind program in
+ // qv4unwindhelper_p-arm.h when changing this list.
+ JSC::ARMRegisters::r12,
+ JSC::ARMRegisters::r10,
+ JSC::ARMRegisters::r9,
+ JSC::ARMRegisters::r8,
+ JSC::ARMRegisters::r7,
+ JSC::ARMRegisters::r6,
+ JSC::ARMRegisters::r5,
+ JSC::ARMRegisters::r4
+};
+#endif
+
+const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]);
+
+/* End of platform/calling convention/architecture specific section */
+
+
+const Assembler::VoidType Assembler::Void;
+
+Assembler::Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine)
+ : _function(function), _vmFunction(vmFunction), _engine(engine), _nextBlock(0)
+{
+}
+
+void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock)
+{
+ _addrs[block] = label();
+ _nextBlock = nextBlock;
+}
+
+void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target)
+{
+ if (target != _nextBlock)
+ _patches[target].append(jump());
+}
+
+void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump)
+{
+ _patches[targetBlock].append(targetJump);
+}
+
+void Assembler::addPatch(DataLabelPtr patch, Label target)
+{
+ DataLabelPatch p;
+ p.dataLabel = patch;
+ p.target = target;
+ _dataLabelPatches.append(p);
+}
+
+void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target)
+{
+ _labelPatches[target].append(patch);
+}
+
+Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t)
+{
+ int32_t offset = 0;
+ int scope = t->scope;
+ QV4::Function *f = _vmFunction;
+ RegisterID context = ContextRegister;
+ if (scope) {
+ loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister);
+ --scope;
+ f = f->outer;
+ context = ScratchRegister;
+ while (scope) {
+ loadPtr(Address(context, offsetof(ExecutionContext, outer)), context);
+ f = f->outer;
+ --scope;
+ }
+ }
+ switch (t->kind) {
+ case V4IR::Temp::Formal:
+ case V4IR::Temp::ScopedFormal: {
+ loadPtr(Address(context, offsetof(CallContext, arguments)), reg);
+ offset = t->index * sizeof(Value);
+ } break;
+ case V4IR::Temp::Local:
+ case V4IR::Temp::ScopedLocal: {
+ loadPtr(Address(context, offsetof(CallContext, locals)), reg);
+ offset = t->index * sizeof(Value);
+ } break;
+ case V4IR::Temp::StackSlot: {
+ assert(t->scope == 0);
+ const int arg = _function->maxNumberOfArguments + t->index + 1;
+ offset = - sizeof(Value) * (arg + 1);
+ offset -= sizeof(void*) * calleeSavedRegisterCount;
+ reg = LocalsRegister;
+ } break;
+ default:
+ Q_UNIMPLEMENTED();
+ }
+ return Pointer(reg, offset);
+}
+
+template <typename Result, typename Source>
+void Assembler::copyValue(Result result, Source source)
+{
+#ifdef VALUE_FITS_IN_REGISTER
+ // Use ReturnValueRegister as "scratch" register because loadArgument
+ // and storeArgument are functions that may need a scratch register themselves.
+ loadArgumentInRegister(source, ReturnValueRegister);
+ storeReturnValue(result);
+#else
+ loadDouble(source, FPGpr0);
+ storeDouble(FPGpr0, result);
+#endif
+}
+
+template <typename Result>
+void Assembler::copyValue(Result result, V4IR::Expr* source)
+{
+#ifdef VALUE_FITS_IN_REGISTER
+ // Use ReturnValueRegister as "scratch" register because loadArgument
+ // and storeArgument are functions that may need a scratch register themselves.
+ loadArgumentInRegister(source, ReturnValueRegister);
+ storeReturnValue(result);
+#else
+ if (V4IR::Temp *temp = source->asTemp()) {
+ loadDouble(temp, FPGpr0);
+ storeDouble(FPGpr0, result);
+ } else if (V4IR::Const *c = source->asConst()) {
+ QV4::Value v = convertToValue(c);
+ storeValue(v, result);
+ } else {
+ assert(! "not implemented");
+ }
+#endif
+}
+
+
+void Assembler::storeValue(QV4::Value value, V4IR::Temp* destination)
+{
+ Address addr = loadTempAddress(ScratchRegister, destination);
+ storeValue(value, addr);
+}
+
+int Assembler::calculateStackFrameSize(int locals)
+{
+ const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry
+ + RegisterSize; // saved StackFrameRegister
+
+ // space for the locals and the callee saved registers
+ int frameSize = locals * sizeof(QV4::Value) + sizeof(void*) * calleeSavedRegisterCount;
+
+ frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise);
+ frameSize -= stackSpaceAllocatedOtherwise;
+
+ return frameSize;
+}
+
+void Assembler::enterStandardStackFrame(int locals)
+{
+ platformEnterStandardStackFrame();
+
+ // ### FIXME: Handle through calleeSavedRegisters mechanism
+ // or eliminate StackFrameRegister altogether.
+ push(StackFrameRegister);
+ move(StackPointerRegister, StackFrameRegister);
+
+ int frameSize = calculateStackFrameSize(locals);
+
+ subPtr(TrustedImm32(frameSize), StackPointerRegister);
+
+ for (int i = 0; i < calleeSavedRegisterCount; ++i)
+ storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*)));
+
+ move(StackFrameRegister, LocalsRegister);
+}
+
+void Assembler::leaveStandardStackFrame(int locals)
+{
+ // restore the callee saved registers
+ for (int i = calleeSavedRegisterCount - 1; i >= 0; --i)
+ loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]);
+
+ int frameSize = calculateStackFrameSize(locals);
+ // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
+ // work well for large immediates.
+#if CPU(ARM_THUMB2)
+ move(TrustedImm32(frameSize), JSC::ARMRegisters::r3);
+ add32(JSC::ARMRegisters::r3, StackPointerRegister);
+#else
+ addPtr(TrustedImm32(frameSize), StackPointerRegister);
+#endif
+
+ pop(StackFrameRegister);
+ platformLeaveStandardStackFrame();
+}
+
+
+
+#define OP(op) \
+ { isel_stringIfy(op), op, 0, 0 }
+
+#define INLINE_OP(op, memOp, immOp) \
+ { isel_stringIfy(op), op, memOp, immOp }
+
+#define NULL_OP \
+ { 0, 0, 0, 0 }
+
+const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::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(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd
+ INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr
+ INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor
+
+ INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd
+ INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub
+ INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul
+
+ OP(__qmljs_div), // OpDiv
+ OP(__qmljs_mod), // OpMod
+
+ INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift
+ INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift
+ INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift
+
+ OP(__qmljs_gt), // OpGt
+ OP(__qmljs_lt), // OpLt
+ OP(__qmljs_ge), // OpGe
+ OP(__qmljs_le), // OpLe
+ OP(__qmljs_eq), // OpEqual
+ OP(__qmljs_ne), // OpNotEqual
+ OP(__qmljs_se), // OpStrictEqual
+ OP(__qmljs_sne), // OpStrictNotEqual
+
+ OP(__qmljs_instanceof), // OpInstanceof
+ OP(__qmljs_in), // OpIn
+
+ NULL_OP, // OpAnd
+ NULL_OP // OpOr
+};
+
+void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right)
+{
+ const BinaryOperationInfo& info = binaryOperations[operation];
+ if (!info.fallbackImplementation) {
+ assert(!"unreachable");
+ return;
+ }
+
+ Value leftConst = Value::undefinedValue();
+ Value rightConst = Value::undefinedValue();
+
+ bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp;
+
+ if (canDoInline) {
+ if (left->asConst()) {
+ leftConst = convertToValue(left->asConst());
+ canDoInline = canDoInline && leftConst.tryIntegerConversion();
+ }
+ if (right->asConst()) {
+ rightConst = convertToValue(right->asConst());
+ canDoInline = canDoInline && rightConst.tryIntegerConversion();
+ }
+ }
+
+ Jump binOpFinished;
+
+ if (canDoInline) {
+
+ Jump leftTypeCheck;
+ if (left->asTemp()) {
+ Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp());
+ typeAddress.offset += offsetof(QV4::Value, tag);
+ leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type));
+ }
+
+ Jump rightTypeCheck;
+ if (right->asTemp()) {
+ Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp());
+ typeAddress.offset += offsetof(QV4::Value, tag);
+ rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type));
+ }
+
+ if (left->asTemp()) {
+ Address leftValue = loadTempAddress(ScratchRegister, left->asTemp());
+ leftValue.offset += offsetof(QV4::Value, int_32);
+ load32(leftValue, IntegerOpRegister);
+ } else { // left->asConst()
+ move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister);
+ }
+
+ Jump overflowCheck;
+
+ if (right->asTemp()) {
+ Address rightValue = loadTempAddress(ScratchRegister, right->asTemp());
+ rightValue.offset += offsetof(QV4::Value, int_32);
+
+ overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister);
+ } else { // right->asConst()
+ overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister);
+ }
+
+ Address resultAddr = loadTempAddress(ScratchRegister, target);
+ Address resultValueAddr = resultAddr;
+ resultValueAddr.offset += offsetof(QV4::Value, int_32);
+ store32(IntegerOpRegister, resultValueAddr);
+
+ Address resultTypeAddr = resultAddr;
+ resultTypeAddr.offset += offsetof(QV4::Value, tag);
+ store32(TrustedImm32(QV4::Value::_Integer_Type), resultTypeAddr);
+
+ binOpFinished = jump();
+
+ if (leftTypeCheck.isSet())
+ leftTypeCheck.link(this);
+ if (rightTypeCheck.isSet())
+ rightTypeCheck.link(this);
+ if (overflowCheck.isSet())
+ overflowCheck.link(this);
+ }
+
+ // Fallback
+ generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, ContextRegister,
+ Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right));
+
+ if (binOpFinished.isSet())
+ binOpFinished.link(this);
+}
+#if OS(LINUX) || OS(MAC_OS_X)
+static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions,
+ const QVector<String*> &identifiers)
+{
+ QByteArray processedOutput(output);
+ for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ it != end; ++it) {
+ QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16);
+ ptrString.prepend("0x");
+ processedOutput = processedOutput.replace(ptrString, it.value());
+ }
+ for (QVector<String*>::ConstIterator it = identifiers.begin(), end = identifiers.end();
+ it != end; ++it) {
+ QByteArray ptrString = QByteArray::number(quintptr(*it), 16);
+ ptrString.prepend("0x");
+ QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\"";
+ processedOutput = processedOutput.replace(ptrString, replacement);
+ }
+ fprintf(stderr, "%s\n", processedOutput.constData());
+}
+#endif
+
+void Assembler::recordLineNumber(int lineNumber)
+{
+ CodeLineNumerMapping mapping;
+ mapping.location = label();
+ mapping.lineNumber = lineNumber;
+ codeLineNumberMappings << mapping;
+}
+
+
+void Assembler::link(QV4::Function *vmFunc)
+{
+ Label endOfCode = label();
+#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS)
+ // Let the ARM exception table follow right after that
+ for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i)
+ nop();
+#endif
+
+ {
+ QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches);
+ while (it.hasNext()) {
+ it.next();
+ V4IR::BasicBlock *block = it.key();
+ Label target = _addrs.value(block);
+ assert(target.isSet());
+ foreach (Jump jump, it.value())
+ jump.linkTo(target, this);
+ }
+ }
+
+ JSC::JSGlobalData dummy(_engine->executableAllocator);
+ JSC::LinkBuffer linkBuffer(dummy, this, 0);
+ vmFunc->codeSize = linkBuffer.offsetOf(endOfCode);
+
+ vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count());
+ for (int i = 0; i < codeLineNumberMappings.count(); ++i) {
+ QV4::LineNumberMapping mapping;
+ mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location);
+ mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber;
+ vmFunc->lineNumberMappings[i] = mapping;
+ }
+
+ QHash<void*, const char*> functions;
+ foreach (CallToLink ctl, _callsToLink) {
+ linkBuffer.link(ctl.call, ctl.externalFunction);
+ functions[ctl.externalFunction.value()] = ctl.functionName;
+ }
+
+ foreach (const DataLabelPatch &p, _dataLabelPatches)
+ linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
+
+ {
+ QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches);
+ while (it.hasNext()) {
+ it.next();
+ V4IR::BasicBlock *block = it.key();
+ Label target = _addrs.value(block);
+ assert(target.isSet());
+ foreach (DataLabelPtr label, it.value())
+ linkBuffer.patch(label, linkBuffer.locationOf(target));
+ }
+ }
+
+#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS)
+ UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode));
+#endif
+
+ static bool showCode = !qgetenv("SHOW_CODE").isNull();
+ if (showCode) {
+#if OS(LINUX) && !defined(Q_OS_ANDROID)
+ char* disasmOutput = 0;
+ size_t disasmLength = 0;
+ FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength);
+ WTF::setDataFile(disasmStream);
+#elif OS(MAC_OS_X)
+ struct MemStream {
+ QByteArray buf;
+ static int write(void *cookie, const char *buf, int len) {
+ MemStream *stream = reinterpret_cast<MemStream *>(cookie);
+ stream->buf.append(buf, len);
+ return len;
+ }
+ };
+ MemStream memStream;
+
+ FILE* disasmStream = fwopen(&memStream, MemStream::write);
+ WTF::setDataFile(disasmStream);
+#endif
+
+ QByteArray name = _function->name->toUtf8();
+ if (name.isEmpty()) {
+ name = QByteArray::number(quintptr(_function), 16);
+ name.prepend("IR::Function(0x");
+ name.append(")");
+ }
+ vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+
+ WTF::setDataFile(stderr);
+#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X)
+ fclose(disasmStream);
+# if OS(MAC_OS_X)
+ char *disasmOutput = memStream.buf.data();
+# endif
+# if CPU(X86) || CPU(X86_64)
+ QHash<void*, String*> idents;
+ printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers);
+# endif
+# if OS(LINUX)
+ free(disasmOutput);
+# endif
+#endif
+ } else {
+ vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ }
+
+ vmFunc->code = (Value (*)(QV4::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress();
+}
+
+InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module)
+ : EvalInstructionSelection(engine, module)
+ , _block(0)
+ , _function(0)
+ , _vmFunction(0)
+ , _as(0)
+ , _locals(0)
+{
+}
+
+InstructionSelection::~InstructionSelection()
+{
+ delete _as;
+}
+
+void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function)
+{
+ QVector<Lookup> lookups;
+ QSet<V4IR::BasicBlock*> reentryBlocks;
+ qSwap(_function, function);
+ qSwap(_vmFunction, vmFunction);
+ qSwap(_lookups, lookups);
+ qSwap(_reentryBlocks, reentryBlocks);
+ Assembler* oldAssembler = _as;
+ _as = new Assembler(_function, _vmFunction, engine());
+
+ V4IR::Optimizer opt(_function);
+ opt.run();
+ if (opt.isInSSA()) {
+#if CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX)) && 0
+ // TODO: add a register allocator here.
+#else
+ // No register allocator available for this platform, so:
+ opt.convertOutOfSSA();
+ ConvertTemps().toStackSlots(_function);
+#endif
+ } else {
+ ConvertTemps().toStackSlots(_function);
+ }
+
+ int locals = (_function->tempCount + _function->maxNumberOfArguments) + 1;
+ locals = (locals + 1) & ~1;
+ qSwap(_locals, locals);
+ _as->enterStandardStackFrame(_locals);
+
+ int contextPointer = 0;
+#if !defined(RETURN_VALUE_IN_REGISTER)
+ // When the return VM value doesn't fit into a register, then
+ // the caller provides a pointer for storage as first argument.
+ // That shifts the index the context pointer argument by one.
+ contextPointer++;
+#endif
+
+#ifdef ARGUMENTS_IN_REGISTERS
+ _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister);
+#else
+ _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister);
+#endif
+
+ for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) {
+ V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0;
+ _block = _function->basicBlocks[i];
+ _as->registerBlock(_block, nextBlock);
+
+ if (_reentryBlocks.contains(_block)) {
+ _as->enterStandardStackFrame(/*locals*/0);
+#ifdef ARGUMENTS_IN_REGISTERS
+ _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister);
+ _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister);
+#else
+ _as->loadPtr(addressForArgument(0), Assembler::ContextRegister);
+ _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister);
+#endif
+ }
+
+ foreach (V4IR::Stmt *s, _block->statements) {
+ if (s->location.isValid())
+ _as->recordLineNumber(s->location.startLine);
+ s->accept(this);
+ }
+ }
+
+ _as->link(_vmFunction);
+
+ if (_lookups.size()) {
+ _vmFunction->lookups = new Lookup[_lookups.size()];
+ memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup));
+ }
+
+ UnwindHelper::registerFunction(_vmFunction);
+
+ qSwap(_vmFunction, vmFunction);
+ qSwap(_function, function);
+ qSwap(_lookups, lookups);
+ qSwap(_reentryBlocks, reentryBlocks);
+ qSwap(_locals, locals);
+ delete _as;
+ _as = oldAssembler;
+}
+
+void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ QV4::String *s = identifier(*func->id);
+
+ if (useFastLookups && func->global) {
+ uint index = addGlobalLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::TrustedImm32(index),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ } else {
+ generateFunctionCall(Assembler::Void, __qmljs_call_activation_property,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ s,
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ }
+}
+
+void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name));
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::Reference(base), Assembler::Reference(index));
+}
+
+void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name));
+}
+
+void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(value));
+}
+
+void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name));
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index));
+}
+
+void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name));
+}
+
+void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
+{
+ _as->storeValue(Value::fromBoolean(false), result);
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name));
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index));
+}
+
+void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), identifier(name));
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment,
+ Assembler::PointerToValue(result), Assembler::PointerToValue(value));
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name));
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base),
+ Assembler::Reference(index));
+}
+
+void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), identifier(name));
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement,
+ Assembler::PointerToValue(result), Assembler::PointerToValue(value));
+}
+
+void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::Reference(arg));
+}
+
+typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr));
+static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody,
+ QV4::String *exceptionVarName, Value *exceptionVar)
+{
+ *exceptionVar = Value::undefinedValue();
+ void *addressToContinueAt = 0;
+ try {
+ addressToContinueAt = tryBody(context, localsPtr);
+ } catch (Exception& ex) {
+ ex.accept(context);
+ *exceptionVar = ex.value();
+ try {
+ ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context);
+ addressToContinueAt = catchBody(catchContext, localsPtr);
+ context = __qmljs_builtin_pop_scope(catchContext);
+ } catch (Exception& ex) {
+ *exceptionVar = ex.value();
+ ex.accept(context);
+ addressToContinueAt = catchBody(context, localsPtr);
+ }
+ }
+ return addressToContinueAt;
+}
+
+void InstructionSelection::visitTry(V4IR::Try *t)
+{
+ // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end
+ // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper
+ // returns and to which we jump to.
+
+ _reentryBlocks.insert(t->tryBlock);
+ _reentryBlocks.insert(t->catchBlock);
+
+ generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister,
+ Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock),
+ identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar));
+ _as->jump(Assembler::ReturnValueRegister);
+}
+
+void InstructionSelection::callBuiltinFinishTry()
+{
+ // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper
+ // with the address that we'd like to continue at, which is right after the ret below.
+ Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister);
+ _as->leaveStandardStackFrame(/*locals*/0);
+ _as->ret();
+ _as->addPatch(continuation, _as->label());
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg));
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg));
+}
+
+void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg)
+{
+ generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPopScope()
+{
+ generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister,
+ Assembler::TrustedImm32(deletable), identifier(name));
+}
+
+void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister,
+ Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter));
+}
+
+void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister,
+ Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value));
+}
+
+void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ int length = prepareVariableArguments(args);
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister,
+ Assembler::PointerToValue(result),
+ baseAddressForCallArguments(), Assembler::TrustedImm32(length));
+}
+
+void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
+{
+ int argc = 0;
+
+ InternalClass *klass = engine()->emptyClass;
+ V4IR::ExprList *it = args;
+ while (it) {
+ V4IR::Name *name = it->expr->asName();
+ it = it->next;
+
+ bool isData = it->expr->asConst()->value;
+ it = it->next;
+ klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
+
+ _as->copyValue(argumentAddressForCall(argc++), it->expr);
+
+ if (!isData) {
+ it = it->next;
+ _as->copyValue(argumentAddressForCall(argc++), it->expr);
+ }
+
+ it = it->next;
+ }
+
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), baseAddressForCallArguments(),
+ Assembler::TrustedImmPtr(klass));
+}
+
+void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ V4IR::Temp* thisObject = 0;
+ generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject),
+ Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::loadThisObject(V4IR::Temp *temp)
+{
+#if defined(VALUE_FITS_IN_REGISTER)
+ _as->load64(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister);
+ _as->storeReturnValue(temp);
+#else
+ _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)));
+#endif
+}
+
+void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp)
+{
+ _as->storeValue(convertToValue(sourceConst), targetTemp);
+}
+
+void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp)
+{
+ Value v = Value::fromString(identifier(str));
+ _as->storeValue(v, targetTemp);
+}
+
+void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp)
+{
+ Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value,
+ sourceRegexp->flags));
+ _vmFunction->generatedValues.append(v);
+ _as->storeValue(v, targetTemp);
+}
+
+void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp)
+{
+ String *propertyName = identifier(*name->id);
+ if (useFastLookups && name->global) {
+ uint index = addGlobalLookup(propertyName);
+ generateLookupCall(index, offsetof(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::PointerToValue(temp));
+ return;
+ }
+ generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName);
+}
+
+void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName)
+{
+ String *propertyName = identifier(targetName);
+ generateFunctionCall(Assembler::Void, __qmljs_set_activation_property,
+ Assembler::ContextRegister, propertyName, Assembler::Reference(source));
+}
+
+void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target)
+{
+ QV4::Function *vmFunc = vmFunction(closure->value);
+ assert(vmFunc);
+ generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc));
+}
+
+void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target)
+{
+ if (useFastLookups) {
+ QV4::String *s = identifier(name);
+ uint index = addLookup(s);
+ generateLookupCall(index, offsetof(QV4::Lookup, getter), Assembler::PointerToValue(target),
+ Assembler::Reference(base));
+ } else {
+ generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target),
+ Assembler::Reference(base), identifier(name));
+ }
+}
+
+void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ if (useFastLookups) {
+ QV4::String *s = identifier(targetName);
+ uint index = addSetterLookup(s);
+ generateLookupCall(index, offsetof(QV4::Lookup, setter), Assembler::Reference(targetBase), Assembler::Reference(source));
+ } else {
+ generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister,
+ Assembler::Reference(targetBase),
+ identifier(targetName), Assembler::Reference(source));
+ }
+}
+
+void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister,
+ Assembler::PointerToValue(target), Assembler::Reference(base),
+ Assembler::Reference(index));
+}
+
+void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister,
+ Assembler::Reference(targetBase), Assembler::Reference(targetIndex),
+ Assembler::Reference(source));
+}
+
+void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ _as->copyValue(targetTemp, sourceTemp);
+}
+
+#define setOp(op, opName, operation) \
+ do { op = operation; opName = isel_stringIfy(operation); } while (0)
+
+void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ UnaryOpName op = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpIfTrue: assert(!"unreachable"); break;
+ case V4IR::OpNot: setOp(op, opName, __qmljs_not); break;
+ case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break;
+ case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break;
+ case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break;
+ case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break;
+ case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break;
+ default: assert(!"unreachable"); break;
+ } // switch
+
+ if (op)
+ _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp),
+ Assembler::Reference(sourceTemp));
+}
+
+void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
+{
+ Q_ASSERT(leftSource->asTemp() && rightSource->asTemp());
+ _as->generateBinOp(oper, target, leftSource->asTemp(), rightSource->asTemp());
+}
+
+void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName)
+{
+ InplaceBinOpName op = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break;
+ case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break;
+ case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break;
+ case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break;
+ case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break;
+ case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break;
+ case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break;
+ case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break;
+ case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break;
+ case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break;
+ case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ if (op) {
+ _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister,
+ identifier(targetName), Assembler::Reference(rightSource));
+ }
+}
+
+void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp)
+{
+ InplaceBinOpElement op = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break;
+ case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break;
+ case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break;
+ case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break;
+ case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break;
+ case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break;
+ case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break;
+ case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break;
+ case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break;
+ case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break;
+ case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (op) {
+ _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister,
+ Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp),
+ Assembler::Reference(source));
+ }
+}
+
+void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName)
+{
+ InplaceBinOpMember op = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break;
+ case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break;
+ case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break;
+ case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break;
+ case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break;
+ case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break;
+ case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break;
+ case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break;
+ case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break;
+ case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break;
+ case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (op) {
+ String* member = identifier(targetName);
+ _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister,
+ Assembler::Reference(targetBase), identifier(targetName),
+ Assembler::Reference(source));
+ }
+}
+
+void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name,
+ V4IR::ExprList *args, V4IR::Temp *result)
+{
+ assert(base != 0);
+
+ int argc = prepareVariableArguments(args);
+ QV4::String *s = identifier(name);
+
+ if (useFastLookups) {
+ uint index = addLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::Reference(base), Assembler::TrustedImm32(index),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ } else {
+ generateFunctionCall(Assembler::Void, __qmljs_call_property,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::Reference(base), s,
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ }
+}
+
+void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ assert(base != 0);
+
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(Assembler::Void, __qmljs_call_element,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::Reference(base), Assembler::Reference(index),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target)
+{
+ // FIXME: do something more useful with this info
+ copyValue(source, target);
+}
+
+String *InstructionSelection::identifier(const QString &s)
+{
+ String *str = engine()->newIdentifier(s);
+ _vmFunction->identifiers.append(str);
+ return str;
+}
+
+void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ assert(func != 0);
+
+ if (useFastLookups && func->global) {
+ int argc = prepareVariableArguments(args);
+ QV4::String *s = identifier(*func->id);
+
+ uint index = addGlobalLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup,
+ Assembler::ContextRegister, Assembler::PointerToValue(result),
+ Assembler::TrustedImm32(index),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+ return;
+ }
+
+ callRuntimeMethod(result, __qmljs_construct_activation_property, func, args);
+}
+
+void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ assert(value != 0);
+
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister,
+ Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::visitJump(V4IR::Jump *s)
+{
+ _as->jumpToBlock(_block, s->target);
+}
+
+void InstructionSelection::visitCJump(V4IR::CJump *s)
+{
+ if (V4IR::Temp *t = s->cond->asTemp()) {
+ Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t);
+ Address tag = temp;
+ tag.offset += offsetof(QV4::Value, tag);
+ Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type));
+
+ Address data = temp;
+ data.offset += offsetof(QV4::Value, int_32);
+ _as->load32(data, Assembler::ReturnValueRegister);
+ Assembler::Jump testBoolean = _as->jump();
+
+ booleanConversion.link(_as);
+ {
+ generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t));
+ }
+
+ testBoolean.link(_as);
+ Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
+ _as->addPatch(s->iftrue, target);
+
+ _as->jumpToBlock(_block, s->iffalse);
+ return;
+ } else if (V4IR::Binop *b = s->cond->asBinop()) {
+ if (b->left->asTemp() && b->right->asTemp()) {
+ CmpOp op = 0;
+ const char *opName = 0;
+ switch (b->op) {
+ default: Q_UNREACHABLE(); assert(!"todo"); break;
+ case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break;
+ case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break;
+ case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break;
+ case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break;
+ case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break;
+ case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break;
+ case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break;
+ case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break;
+ case V4IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break;
+ case V4IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break;
+ } // switch
+
+ _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, Assembler::ContextRegister,
+ Assembler::Reference(b->left->asTemp()),
+ Assembler::Reference(b->right->asTemp()));
+
+ Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
+ _as->addPatch(s->iftrue, target);
+
+ _as->jumpToBlock(_block, s->iffalse);
+ return;
+ } else {
+ assert(!"wip");
+ }
+ Q_UNIMPLEMENTED();
+ }
+ Q_UNIMPLEMENTED();
+ assert(!"TODO");
+}
+
+void InstructionSelection::visitRet(V4IR::Ret *s)
+{
+ if (V4IR::Temp *t = s->expr->asTemp()) {
+#if defined(RETURN_VALUE_IN_REGISTER)
+#if CPU(X86)
+ Address addr = _as->loadTempAddress(Assembler::ScratchRegister, t);
+ _as->load32(addr, JSC::X86Registers::eax);
+ addr.offset += 4;
+ _as->load32(addr, JSC::X86Registers::edx);
+#else
+ _as->copyValue(Assembler::ReturnValueRegister, t);
+#endif
+#else
+ _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister);
+ _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t);
+#endif
+ } else if (V4IR::Const *c = s->expr->asConst()) {
+ _as->copyValue(Assembler::ReturnValueRegister, c);
+ } else {
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ Q_UNUSED(s);
+ }
+
+ _as->leaveStandardStackFrame(_locals);
+#if !defined(ARGUMENTS_IN_REGISTERS) && !defined(RETURN_VALUE_IN_REGISTER)
+ // Emulate ret(n) instruction
+ // Pop off return address into scratch register ...
+ _as->pop(Assembler::ScratchRegister);
+ // ... and overwrite the invisible argument with
+ // the return address.
+ _as->poke(Assembler::ScratchRegister);
+#endif
+ _as->ret();
+}
+
+int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args)
+{
+ int argc = 0;
+ for (V4IR::ExprList *it = args; it; it = it->next) {
+ ++argc;
+ }
+
+ int i = 0;
+ for (V4IR::ExprList *it = args; it; it = it->next, ++i) {
+// V4IR::Temp *arg = it->expr->asTemp();
+// assert(arg != 0);
+ _as->copyValue(argumentAddressForCall(i), it->expr);
+ }
+
+ return argc;
+}
+
+void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args)
+{
+ V4IR::Name *baseName = base->asName();
+ assert(baseName != 0);
+
+ int argc = prepareVariableArguments(args);
+ _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result),
+ identifier(*baseName->id), baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+}
+
+
+uint InstructionSelection::addLookup(QV4::String *name)
+{
+ uint index = (uint)_lookups.size();
+ QV4::Lookup l;
+ l.getter = Lookup::getterGeneric;
+ for (int i = 0; i < Lookup::Size; ++i)
+ l.classList[i] = 0;
+ l.level = -1;
+ l.index = UINT_MAX;
+ l.name = name;
+ _lookups.append(l);
+ return index;
+}
+
+uint InstructionSelection::addSetterLookup(QV4::String *name)
+{
+ uint index = (uint)_lookups.size();
+ QV4::Lookup l;
+ l.setter = Lookup::setterGeneric;
+ for (int i = 0; i < Lookup::Size; ++i)
+ l.classList[i] = 0;
+ l.level = -1;
+ l.index = UINT_MAX;
+ l.name = name;
+ _lookups.append(l);
+ return index;
+}
+
+uint InstructionSelection::addGlobalLookup(QV4::String *name)
+{
+ uint index = (uint)_lookups.size();
+ QV4::Lookup l;
+ l.globalGetter = Lookup::globalGetterGeneric;
+ for (int i = 0; i < Lookup::Size; ++i)
+ l.classList[i] = 0;
+ l.level = -1;
+ l.index = UINT_MAX;
+ l.name = name;
+ _lookups.append(l);
+ return index;
+}
diff --git a/src/qml/qml/v4/qv4isel_masm_p.h b/src/qml/qml/v4/qv4isel_masm_p.h
new file mode 100644
index 0000000000..b53b0e5c74
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_masm_p.h
@@ -0,0 +1,938 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ISEL_MASM_P_H
+#define QV4ISEL_MASM_P_H
+
+#include "qv4global_p.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4isel_util_p.h"
+#include "qv4object_p.h"
+#include "qv4runtime_p.h"
+#include "qv4lookup_p.h"
+
+#include <QtCore/QHash>
+#include <config.h>
+#include <wtf/Vector.h>
+#include <assembler/MacroAssembler.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace MASM {
+
+class Assembler : public JSC::MacroAssembler
+{
+public:
+ Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine);
+#if CPU(X86)
+
+#undef VALUE_FITS_IN_REGISTER
+#undef ARGUMENTS_IN_REGISTERS
+
+#if OS(WINDOWS)
+ // Returned in EAX:EDX pair
+#define RETURN_VALUE_IN_REGISTER
+#else
+#undef RETURN_VALUE_IN_REGISTER
+#endif
+
+#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
+
+ static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
+ static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
+ static const RegisterID LocalsRegister = JSC::X86Registers::edi;
+ static const RegisterID ContextRegister = JSC::X86Registers::esi;
+ static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
+ static const RegisterID ScratchRegister = JSC::X86Registers::ecx;
+ static const RegisterID IntegerOpRegister = JSC::X86Registers::eax;
+ static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
+
+ static const int RegisterSize = 4;
+
+ static const int RegisterArgumentCount = 0;
+ static RegisterID registerForArgument(int)
+ {
+ assert(false);
+ // Not reached.
+ return JSC::X86Registers::eax;
+ }
+
+ // Return address is pushed onto stack by the CPU.
+ static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize;
+ static const int StackShadowSpace = 0;
+ inline void platformEnterStandardStackFrame() {}
+ inline void platformLeaveStandardStackFrame() {}
+#elif CPU(X86_64)
+
+#define VALUE_FITS_IN_REGISTER
+#define ARGUMENTS_IN_REGISTERS
+#define RETURN_VALUE_IN_REGISTER
+#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1
+
+ static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
+ static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
+ static const RegisterID LocalsRegister = JSC::X86Registers::r12;
+ static const RegisterID ContextRegister = JSC::X86Registers::r14;
+ static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
+ static const RegisterID ScratchRegister = JSC::X86Registers::r10;
+ static const RegisterID IntegerOpRegister = JSC::X86Registers::eax;
+ static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
+
+ static const int RegisterSize = 8;
+
+#if OS(WINDOWS)
+ 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
+ };
+ assert(index >= 0 && index < RegisterArgumentCount);
+ return regs[index];
+ };
+ static const int StackShadowSpace = 32;
+#else // Unix
+ 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
+ };
+ assert(index >= 0 && index < RegisterArgumentCount);
+ return regs[index];
+ };
+ static const int StackShadowSpace = 0;
+#endif
+
+ // Return address is pushed onto stack by the CPU.
+ static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize;
+ inline void platformEnterStandardStackFrame() {}
+ inline void platformLeaveStandardStackFrame() {}
+#elif CPU(ARM)
+
+#undef VALUE_FITS_IN_REGISTER
+#define ARGUMENTS_IN_REGISTERS
+#undef RETURN_VALUE_IN_REGISTER
+#undef HAVE_ALU_OPS_WITH_MEM_OPERAND
+
+ static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4;
+ static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp;
+ static const RegisterID LocalsRegister = JSC::ARMRegisters::r7;
+ static const RegisterID ContextRegister = JSC::ARMRegisters::r5;
+ static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0;
+ static const RegisterID ScratchRegister = JSC::ARMRegisters::r6;
+ static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0;
+ static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0;
+
+ static const int RegisterSize = 4;
+
+ static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0;
+ static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1;
+ static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2;
+ static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3;
+
+ static const int RegisterArgumentCount = 4;
+ static RegisterID registerForArgument(int index)
+ {
+ assert(index >= 0 && index < RegisterArgumentCount);
+ return static_cast<RegisterID>(JSC::ARMRegisters::r0 + index);
+ };
+
+ // Registers saved in platformEnterStandardStackFrame below.
+ static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize;
+ static const int StackShadowSpace = 0;
+ inline void platformEnterStandardStackFrame()
+ {
+ // Move the register arguments onto the stack as if they were
+ // pushed by the caller, just like on ia32. This gives us consistent
+ // access to the parameters if we need to.
+ push(JSC::ARMRegisters::r3);
+ push(JSC::ARMRegisters::r2);
+ push(JSC::ARMRegisters::r1);
+ push(JSC::ARMRegisters::r0);
+ push(JSC::ARMRegisters::lr);
+ }
+ inline void platformLeaveStandardStackFrame()
+ {
+ pop(JSC::ARMRegisters::lr);
+ addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister);
+ }
+#else
+#error The JIT needs to be ported to this platform.
+#endif
+ static const int calleeSavedRegisterCount;
+
+#if CPU(X86) || CPU(X86_64)
+ static const int StackAlignment = 16;
+#elif CPU(ARM)
+ // Per AAPCS
+ static const int StackAlignment = 8;
+#else
+#error Stack alignment unknown for this platform.
+#endif
+
+ // 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)
+ {}
+ };
+
+ struct VoidType { VoidType() {} };
+ static const VoidType Void;
+
+
+ typedef JSC::FunctionPtr FunctionPtr;
+
+ struct CallToLink {
+ Call call;
+ FunctionPtr externalFunction;
+ const char* functionName;
+ };
+ struct PointerToValue {
+ PointerToValue(V4IR::Temp *value) : value(value) {}
+ V4IR::Temp *value;
+ };
+ struct Reference {
+ Reference(V4IR::Temp *value) : value(value) {}
+ V4IR::Temp *value;
+ };
+
+ struct ReentryBlock {
+ ReentryBlock(V4IR::BasicBlock *b) : block(b) {}
+ V4IR::BasicBlock *block;
+ };
+
+ void callAbsolute(const char* functionName, FunctionPtr function) {
+ CallToLink ctl;
+ ctl.call = call();
+ ctl.externalFunction = function;
+ ctl.functionName = functionName;
+ _callsToLink.append(ctl);
+ }
+
+ void callAbsolute(const char* /*functionName*/, Address addr) {
+ call(addr);
+ }
+
+ void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock);
+ void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target);
+ void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump);
+ void addPatch(DataLabelPtr patch, Label target);
+ void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target);
+
+ Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t);
+
+ void loadArgumentInRegister(RegisterID source, RegisterID dest)
+ {
+ move(source, dest);
+ }
+
+ void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest)
+ {
+ move(TrustedImmPtr(ptr), dest);
+ }
+
+ void loadArgumentInRegister(const Pointer& ptr, RegisterID dest)
+ {
+ addPtr(TrustedImm32(ptr.offset), ptr.base, dest);
+ }
+
+ void loadArgumentInRegister(PointerToValue temp, RegisterID dest)
+ {
+ if (!temp.value) {
+ loadArgumentInRegister(TrustedImmPtr(0), dest);
+ } else {
+ Pointer addr = loadTempAddress(dest, temp.value);
+ loadArgumentInRegister(addr, dest);
+ }
+ }
+
+ void loadArgumentInRegister(Reference temp, RegisterID dest)
+ {
+ assert(temp.value);
+ Pointer addr = loadTempAddress(dest, temp.value);
+ loadArgumentInRegister(addr, dest);
+ }
+
+ void loadArgumentInRegister(ReentryBlock block, RegisterID dest)
+ {
+ assert(block.block);
+ DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest);
+ addPatch(patch, block.block);
+ }
+
+#ifdef VALUE_FITS_IN_REGISTER
+ void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest)
+ {
+ if (!temp) {
+ QV4::Value undefined = QV4::Value::undefinedValue();
+ move(TrustedImm64(undefined.val), dest);
+ } else {
+ Pointer addr = loadTempAddress(dest, temp);
+ load64(addr, dest);
+ }
+ }
+
+ void loadArgumentInRegister(V4IR::Const* c, RegisterID dest)
+ {
+ QV4::Value v = convertToValue(c);
+ move(TrustedImm64(v.val), dest);
+ }
+
+ void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest)
+ {
+ if (!expr) {
+ QV4::Value undefined = QV4::Value::undefinedValue();
+ move(TrustedImm64(undefined.val), dest);
+ } else if (expr->asTemp()){
+ loadArgumentInRegister(expr->asTemp(), dest);
+ } else if (expr->asConst()) {
+ loadArgumentInRegister(expr->asConst(), dest);
+ } else {
+ assert(!"unimplemented expression type in loadArgument");
+ }
+ }
+#else
+ void loadArgumentInRegister(V4IR::Expr*, RegisterID)
+ {
+ assert(!"unimplemented: expression in loadArgument");
+ }
+#endif
+
+ void loadArgumentInRegister(QV4::String* string, RegisterID dest)
+ {
+ loadArgumentInRegister(TrustedImmPtr(string), dest);
+ }
+
+ void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest)
+ {
+ xorPtr(dest, dest);
+ if (imm32.m_value)
+ move(imm32, dest);
+ }
+
+ void storeReturnValue(RegisterID dest)
+ {
+ move(ReturnValueRegister, dest);
+ }
+
+#ifdef VALUE_FITS_IN_REGISTER
+ void storeReturnValue(const Pointer &dest)
+ {
+ store64(ReturnValueRegister, dest);
+ }
+
+ void storeReturnValue(V4IR::Temp *temp)
+ {
+ if (!temp)
+ return;
+ Pointer addr = loadTempAddress(ScratchRegister, temp);
+ storeReturnValue(addr);
+ }
+#endif
+
+ void storeReturnValue(VoidType)
+ {
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(RegisterID reg)
+ {
+ poke(reg, StackSlot);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(TrustedImm32 value)
+ {
+ poke(value, StackSlot);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(const Pointer& ptr)
+ {
+ addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister);
+ poke(ScratchRegister, StackSlot);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(PointerToValue temp)
+ {
+ if (temp.value) {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp.value);
+ loadArgumentOnStack<StackSlot>(ptr);
+ } else {
+ poke(TrustedImmPtr(0), StackSlot);
+ }
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(Reference temp)
+ {
+ assert (temp.value);
+
+ Pointer ptr = loadTempAddress(ScratchRegister, temp.value);
+ loadArgumentOnStack<StackSlot>(ptr);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(ReentryBlock block)
+ {
+ assert(block.block);
+ DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister);
+ poke(ScratchRegister, StackSlot);
+ addPatch(patch, block.block);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(TrustedImmPtr ptr)
+ {
+ move(TrustedImmPtr(ptr), ScratchRegister);
+ poke(ScratchRegister, StackSlot);
+ }
+
+ template <int StackSlot>
+ void loadArgumentOnStack(QV4::String* name)
+ {
+ poke(TrustedImmPtr(name), StackSlot);
+ }
+
+ using JSC::MacroAssembler::loadDouble;
+ void loadDouble(V4IR::Temp* temp, FPRegisterID dest)
+ {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp);
+ loadDouble(ptr, dest);
+ }
+
+ using JSC::MacroAssembler::storeDouble;
+ void storeDouble(FPRegisterID source, V4IR::Temp* temp)
+ {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp);
+ storeDouble(source, ptr);
+ }
+
+ template <typename Result, typename Source>
+ void copyValue(Result result, Source source);
+ template <typename Result>
+ void copyValue(Result result, V4IR::Expr* source);
+
+ void storeValue(QV4::Value value, Address destination)
+ {
+#ifdef VALUE_FITS_IN_REGISTER
+ store64(TrustedImm64(value.val), destination);
+#else
+ store32(TrustedImm32(value.int_32), destination);
+ destination.offset += 4;
+ store32(TrustedImm32(value.tag), destination);
+#endif
+ }
+
+ void storeValue(QV4::Value value, V4IR::Temp* temp);
+
+ static int calculateStackFrameSize(int locals);
+ void enterStandardStackFrame(int locals);
+ void leaveStandardStackFrame(int locals);
+
+ template <int argumentNumber, typename T>
+ void loadArgumentOnStackOrRegister(const T &value)
+ {
+ if (argumentNumber < RegisterArgumentCount)
+ loadArgumentInRegister(value, registerForArgument(argumentNumber));
+ else
+#if OS(WINDOWS) && CPU(X86_64)
+ loadArgumentOnStack<argumentNumber>(value);
+#else // Sanity:
+ loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value);
+#endif
+ }
+
+ 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, QT_POINTER_SIZE, 0>::Chosen };
+ };
+
+ template <int ArgumentIndex>
+ struct SizeOnStack<ArgumentIndex, VoidType>
+ {
+ enum { Size = 0 };
+ };
+
+
+ template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
+ void generateFunctionCallImp(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) {
+ stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded);
+ sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister);
+ }
+
+ loadArgumentOnStackOrRegister<5>(arg6);
+ loadArgumentOnStackOrRegister<4>(arg5);
+ loadArgumentOnStackOrRegister<3>(arg4);
+ loadArgumentOnStackOrRegister<2>(arg3);
+ loadArgumentOnStackOrRegister<1>(arg2);
+ loadArgumentOnStackOrRegister<0>(arg1);
+
+ callAbsolute(functionName, function);
+
+ storeReturnValue(r);
+
+ if (stackSpaceNeeded)
+ add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister);
+ }
+
+ template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType());
+ }
+
+ template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType());
+ }
+
+ template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType());
+ }
+
+ template <typename ArgRet, typename Callable, typename Arg1, typename Arg2>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType());
+ }
+
+ template <typename ArgRet, typename Callable, typename Arg1>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType());
+ }
+
+ typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID);
+ typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID);
+
+ struct BinaryOperationInfo {
+ const char *name;
+ QV4::BinOp fallbackImplementation;
+ MemRegBinOp inlineMemRegOp;
+ ImmRegBinOp inlineImmRegOp;
+ };
+
+ static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1];
+
+ void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right);
+
+ Jump inline_add32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ return branchAdd32(Overflow, addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ return branchAdd32(Overflow, ScratchRegister, reg);
+#endif
+ }
+
+ Jump inline_add32(TrustedImm32 imm, RegisterID reg)
+ {
+ return branchAdd32(Overflow, imm, reg);
+ }
+
+ Jump inline_sub32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ return branchSub32(Overflow, addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ return branchSub32(Overflow, ScratchRegister, reg);
+#endif
+ }
+
+ Jump inline_sub32(TrustedImm32 imm, RegisterID reg)
+ {
+ return branchSub32(Overflow, imm, reg);
+ }
+
+ Jump inline_mul32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ return branchMul32(Overflow, addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ return branchMul32(Overflow, ScratchRegister, reg);
+#endif
+ }
+
+ Jump inline_mul32(TrustedImm32 imm, RegisterID reg)
+ {
+ return branchMul32(Overflow, imm, reg, reg);
+ }
+
+ Jump inline_shl32(Address addr, RegisterID reg)
+ {
+ load32(addr, ScratchRegister);
+ and32(TrustedImm32(0x1f), ScratchRegister);
+ lshift32(ScratchRegister, reg);
+ return Jump();
+ }
+
+ Jump inline_shl32(TrustedImm32 imm, RegisterID reg)
+ {
+ imm.m_value &= 0x1f;
+ lshift32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_shr32(Address addr, RegisterID reg)
+ {
+ load32(addr, ScratchRegister);
+ and32(TrustedImm32(0x1f), ScratchRegister);
+ rshift32(ScratchRegister, reg);
+ return Jump();
+ }
+
+ Jump inline_shr32(TrustedImm32 imm, RegisterID reg)
+ {
+ imm.m_value &= 0x1f;
+ rshift32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_ushr32(Address addr, RegisterID reg)
+ {
+ load32(addr, ScratchRegister);
+ and32(TrustedImm32(0x1f), ScratchRegister);
+ urshift32(ScratchRegister, reg);
+ return branchTest32(Signed, reg, reg);
+ }
+
+ Jump inline_ushr32(TrustedImm32 imm, RegisterID reg)
+ {
+ imm.m_value &= 0x1f;
+ urshift32(imm, reg);
+ return branchTest32(Signed, reg, reg);
+ }
+
+ Jump inline_and32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ and32(addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ and32(ScratchRegister, reg);
+#endif
+ return Jump();
+ }
+
+ Jump inline_and32(TrustedImm32 imm, RegisterID reg)
+ {
+ and32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_or32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ or32(addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ or32(ScratchRegister, reg);
+#endif
+ return Jump();
+ }
+
+ Jump inline_or32(TrustedImm32 imm, RegisterID reg)
+ {
+ or32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_xor32(Address addr, RegisterID reg)
+ {
+#if HAVE(ALU_OPS_WITH_MEM_OPERAND)
+ xor32(addr, reg);
+#else
+ load32(addr, ScratchRegister);
+ xor32(ScratchRegister, reg);
+#endif
+ return Jump();
+ }
+
+ Jump inline_xor32(TrustedImm32 imm, RegisterID reg)
+ {
+ xor32(imm, reg);
+ return Jump();
+ }
+
+ void link(QV4::Function *vmFunc);
+
+ void recordLineNumber(int lineNumber);
+
+private:
+ V4IR::Function *_function;
+ QV4::Function *_vmFunction;
+ QHash<V4IR::BasicBlock *, Label> _addrs;
+ QHash<V4IR::BasicBlock *, QVector<Jump> > _patches;
+ QList<CallToLink> _callsToLink;
+
+ struct DataLabelPatch {
+ DataLabelPtr dataLabel;
+ Label target;
+ };
+ QList<DataLabelPatch> _dataLabelPatches;
+
+ QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
+ V4IR::BasicBlock *_nextBlock;
+
+ QV4::ExecutionEngine *_engine;
+
+ struct CodeLineNumerMapping
+ {
+ Assembler::Label location;
+ int lineNumber;
+ };
+ QVector<CodeLineNumerMapping> codeLineNumberMappings;
+};
+
+class Q_QML_EXPORT InstructionSelection:
+ protected V4IR::IRDecoder,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(QV4::Function *vmFunction, V4IR::Function *function);
+
+protected:
+ virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinDeleteValue(V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result);
+ virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result);
+ virtual void callBuiltinThrow(V4IR::Temp *arg);
+ virtual void callBuiltinFinishTry();
+ virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result);
+ virtual void callBuiltinPushWithScope(V4IR::Temp *arg);
+ virtual void callBuiltinPopScope();
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name);
+ virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter);
+ virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value);
+ virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args);
+ virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void convertType(V4IR::Temp *source, V4IR::Temp *target);
+ virtual void loadThisObject(V4IR::Temp *temp);
+ virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp);
+ virtual void loadString(const QString &str, V4IR::Temp *targetTemp);
+ virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp);
+ virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp);
+ virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName);
+ virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target);
+ virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target);
+ virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target);
+ virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex);
+ virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp);
+ virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target);
+ virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName);
+ virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName);
+
+ typedef Assembler::Address Address;
+ typedef Assembler::Pointer Pointer;
+
+ Address addressForArgument(int index) const
+ {
+ // StackFrameRegister 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(Assembler::StackFrameRegister, (index + 2) * sizeof(void*));
+ }
+
+ // Some run-time functions take (Value* args, int argc). This function is for populating
+ // the args.
+ Pointer argumentAddressForCall(int argument)
+ {
+ const int index = _function->maxNumberOfArguments - argument;
+ return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index)
+ - sizeof(void*) // size of ebp
+ - sizeof(void*) * Assembler::calleeSavedRegisterCount
+ );
+ }
+ Pointer baseAddressForCallArguments()
+ {
+ return argumentAddressForCall(0);
+ }
+
+ QV4::String *identifier(const QString &s);
+ virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result);
+ virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result);
+
+ virtual void visitJump(V4IR::Jump *);
+ virtual void visitCJump(V4IR::CJump *);
+ virtual void visitRet(V4IR::Ret *);
+ virtual void visitTry(V4IR::Try *);
+
+private:
+ #define isel_stringIfyx(s) #s
+ #define isel_stringIfy(s) isel_stringIfyx(s)
+
+ #define generateFunctionCall(t, function, ...) \
+ _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__)
+
+ int prepareVariableArguments(V4IR::ExprList* args);
+
+ typedef void (*ActivationMethod)(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc);
+ void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args);
+#define callRuntimeMethod(result, function, ...) \
+ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__)
+
+ uint addLookup(QV4::String *name);
+ uint addSetterLookup(QV4::String *name);
+ uint addGlobalLookup(QV4::String *name);
+
+ template <typename Arg1, typename Arg2>
+ void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
+ {
+ _as->loadPtr(Assembler::Address(Assembler::ContextRegister, offsetof(QV4::ExecutionContext, lookups)),
+ Assembler::ReturnValueRegister);
+
+ Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup));
+
+ Assembler::Address getterSetter = lookupAddr;
+ getterSetter.offset += getterSetterOffset;
+
+ _as->generateFunctionCallImp(Assembler::Void, "lookup getter/setter", getterSetter, lookupAddr, arg1, arg2);
+ }
+
+ template <typename Arg1>
+ void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1)
+ {
+ generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType());
+ }
+
+ V4IR::BasicBlock *_block;
+ V4IR::Function* _function;
+ QV4::Function* _vmFunction;
+ QVector<QV4::Lookup> _lookups;
+ Assembler* _as;
+ QSet<V4IR::BasicBlock*> _reentryBlocks;
+ int _locals;
+};
+
+class Q_QML_EXPORT ISelFactory: public EvalISelFactory
+{
+public:
+ virtual ~ISelFactory() {}
+ virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module)
+ { return new InstructionSelection(engine, module); }
+ virtual bool jitCompileRegexps() const
+ { return true; }
+};
+
+} // end of namespace MASM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ISEL_MASM_P_H
diff --git a/src/qml/qml/v4/qv4isel_p.cpp b/src/qml/qml/v4/qv4isel_p.cpp
new file mode 100644
index 0000000000..ca8d249f9f
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_p.cpp
@@ -0,0 +1,440 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4debugging_p.h"
+#include "qv4engine_p.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4isel_util_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+
+#include <QString>
+
+#include <cassert>
+
+namespace {
+QTextStream qout(stderr, QIODevice::WriteOnly);
+} // anonymous namespace
+
+using namespace QQmlJS;
+using namespace QQmlJS::V4IR;
+
+EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutionEngine *engine, Module *module)
+ : _engine(engine)
+ , useFastLookups(true)
+{
+ assert(engine);
+ assert(module);
+
+ createFunctionMapping(0, module->rootFunction);
+ foreach (V4IR::Function *f, module->functions) {
+ assert(_irToVM.contains(f));
+ }
+}
+
+EvalInstructionSelection::~EvalInstructionSelection()
+{}
+
+EvalISelFactory::~EvalISelFactory()
+{}
+
+QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *outer, Function *irFunction)
+{
+ QV4::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString());
+ _irToVM.insert(irFunction, vmFunction);
+
+ vmFunction->hasDirectEval = irFunction->hasDirectEval;
+ vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject;
+ vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty();
+ vmFunction->isStrict = irFunction->isStrict;
+ vmFunction->outer = outer;
+ vmFunction->isNamedExpression = irFunction->isNamedExpression;
+ vmFunction->sourceFile = irFunction->sourceFile;
+
+ if (outer)
+ outer->nestedFunctions.append(vmFunction);
+
+ foreach (const QString *formal, irFunction->formals)
+ if (formal)
+ vmFunction->formals.append(_engine->newString(*formal));
+ foreach (const QString *local, irFunction->locals)
+ if (local)
+ vmFunction->locals.append(_engine->newString(*local));
+
+ foreach (V4IR::Function *function, irFunction->nestedFunctions)
+ createFunctionMapping(vmFunction, function);
+
+ return vmFunction;
+}
+
+QV4::Function *EvalInstructionSelection::vmFunction(Function *f) {
+ QV4::Function *function = _irToVM[f];
+ if (!function->code)
+ run(function, f);
+ return function;
+}
+
+void IRDecoder::visitMove(V4IR::Move *s)
+{
+ if (s->op == V4IR::OpInvalid) {
+ if (V4IR::Name *n = s->target->asName()) {
+ if (s->source->asTemp()) {
+ setActivationProperty(s->source->asTemp(), *n->id);
+ return;
+ }
+ } else if (V4IR::Temp *t = s->target->asTemp()) {
+ if (V4IR::Name *n = s->source->asName()) {
+ if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin.
+ loadThisObject(t);
+ else
+ getActivationProperty(n, t);
+ return;
+ } else if (V4IR::Const *c = s->source->asConst()) {
+ loadConst(c, t);
+ return;
+ } else if (V4IR::Temp *t2 = s->source->asTemp()) {
+ copyValue(t2, t);
+ return;
+ } else if (V4IR::String *str = s->source->asString()) {
+ loadString(*str->value, t);
+ return;
+ } else if (V4IR::RegExp *re = s->source->asRegExp()) {
+ loadRegexp(re, t);
+ return;
+ } else if (V4IR::Closure *clos = s->source->asClosure()) {
+ initClosure(clos, t);
+ return;
+ } else if (V4IR::New *ctor = s->source->asNew()) {
+ if (Name *func = ctor->base->asName()) {
+ constructActivationProperty(func, ctor->args, t);
+ return;
+ } else if (V4IR::Member *member = ctor->base->asMember()) {
+ constructProperty(member->base->asTemp(), *member->name, ctor->args, t);
+ return;
+ } else if (V4IR::Temp *value = ctor->base->asTemp()) {
+ constructValue(value, ctor->args, t);
+ return;
+ }
+ } else if (V4IR::Member *m = s->source->asMember()) {
+ if (V4IR::Temp *base = m->base->asTemp()) {
+ getProperty(base, *m->name, t);
+ return;
+ }
+ } else if (V4IR::Subscript *ss = s->source->asSubscript()) {
+ getElement(ss->base->asTemp(), ss->index->asTemp(), t);
+ return;
+ } else if (V4IR::Unop *u = s->source->asUnop()) {
+ if (V4IR::Temp *e = u->expr->asTemp()) {
+ unop(u->op, e, t);
+ return;
+ }
+ } else if (V4IR::Binop *b = s->source->asBinop()) {
+ binop(b->op, b->left, b->right, t);
+ return;
+ } else if (V4IR::Call *c = s->source->asCall()) {
+ if (c->base->asName()) {
+ callBuiltin(c, t);
+ return;
+ } else if (Member *member = c->base->asMember()) {
+ Q_ASSERT(member->base->asTemp());
+ callProperty(member->base->asTemp(), *member->name, c->args, t);
+ return;
+ } else if (Subscript *s = c->base->asSubscript()) {
+ Q_ASSERT(s->base->asTemp());
+ Q_ASSERT(s->index->asTemp());
+ callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, t);
+ return;
+ } else if (V4IR::Temp *value = c->base->asTemp()) {
+ callValue(value, c->args, t);
+ return;
+ }
+ } else if (V4IR::Convert *c = s->source->asConvert()) {
+ Q_ASSERT(c->expr->asTemp());
+ convertType(c->expr->asTemp(), t);
+ return;
+ }
+ } else if (V4IR::Member *m = s->target->asMember()) {
+ if (V4IR::Temp *base = m->base->asTemp()) {
+ if (s->source->asTemp()) {
+ setProperty(s->source->asTemp(), base, *m->name);
+ return;
+ }
+ }
+ } else if (V4IR::Subscript *ss = s->target->asSubscript()) {
+ if (s->source->asTemp()) {
+ setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp());
+ return;
+ }
+ }
+ } else {
+ // inplace assignment, e.g. x += 1, ++x, ...
+ if (V4IR::Temp *t = s->target->asTemp()) {
+ if (s->source->asTemp()) {
+ binop(s->op, t, s->source->asTemp(), t);
+ return;
+ }
+ } else if (V4IR::Name *n = s->target->asName()) {
+ if (s->source->asTemp()) {
+ inplaceNameOp(s->op, s->source->asTemp(), *n->id);
+ return;
+ }
+ } else if (V4IR::Subscript *ss = s->target->asSubscript()) {
+ if (s->source->asTemp()) {
+ inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(),
+ ss->index->asTemp());
+ return;
+ }
+ } else if (V4IR::Member *m = s->target->asMember()) {
+ if (s->source->asTemp()) {
+ inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name);
+ return;
+ }
+ }
+ }
+
+ // For anything else...:
+ Q_UNIMPLEMENTED();
+ s->dump(qout, V4IR::Stmt::MIR);
+ qout << endl;
+ assert(!"TODO");
+}
+
+IRDecoder::~IRDecoder()
+{
+}
+
+void IRDecoder::visitExp(V4IR::Exp *s)
+{
+ if (V4IR::Call *c = s->expr->asCall()) {
+ // These are calls where the result is ignored.
+ if (c->base->asName()) {
+ callBuiltin(c, 0);
+ } else if (Temp *value = c->base->asTemp()) {
+ callValue(value, c->args, 0);
+ } else if (Member *member = c->base->asMember()) {
+ Q_ASSERT(member->base->asTemp());
+ callProperty(member->base->asTemp(), *member->name, c->args, 0);
+ } else if (Subscript *s = c->base->asSubscript()) {
+ Q_ASSERT(s->base->asTemp());
+ Q_ASSERT(s->index->asTemp());
+ callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0);
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+}
+
+void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result)
+{
+ V4IR::Name *baseName = call->base->asName();
+ assert(baseName != 0);
+
+ switch (baseName->builtin) {
+ case V4IR::Name::builtin_invalid:
+ callBuiltinInvalid(baseName, call->args, result);
+ return;
+
+ case V4IR::Name::builtin_typeof: {
+ if (V4IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinTypeofMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (V4IR::Name *n = call->args->expr->asName()) {
+ callBuiltinTypeofName(*n->id, result);
+ return;
+ } else if (V4IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinTypeofValue(arg, result);
+ return;
+ }
+ } break;
+
+ case V4IR::Name::builtin_delete: {
+ if (V4IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinDeleteMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (V4IR::Name *n = call->args->expr->asName()) {
+ callBuiltinDeleteName(*n->id, result);
+ return;
+ } else if (call->args->expr->asTemp()){
+ // TODO: should throw in strict mode
+ callBuiltinDeleteValue(result);
+ return;
+ }
+ } break;
+
+ case V4IR::Name::builtin_postincrement: {
+ if (V4IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (V4IR::Name *n = call->args->expr->asName()) {
+ callBuiltinPostIncrementName(*n->id, result);
+ return;
+ } else if (V4IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinPostIncrementValue(arg, result);
+ return;
+ }
+ } break;
+
+ case V4IR::Name::builtin_postdecrement: {
+ if (V4IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (V4IR::Name *n = call->args->expr->asName()) {
+ callBuiltinPostDecrementName(*n->id, result);
+ return;
+ } else if (V4IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinPostDecrementValue(arg, result);
+ return;
+ }
+ } break;
+
+ case V4IR::Name::builtin_throw: {
+ V4IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinThrow(arg);
+ } return;
+
+ case V4IR::Name::builtin_finish_try:
+ callBuiltinFinishTry();
+ return;
+
+ case V4IR::Name::builtin_foreach_iterator_object: {
+ V4IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinForeachIteratorObject(arg, result);
+ } return;
+
+ case V4IR::Name::builtin_foreach_next_property_name: {
+ V4IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinForeachNextPropertyname(arg, result);
+ } return;
+ case V4IR::Name::builtin_push_with_scope: {
+ V4IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinPushWithScope(arg);
+ } return;
+
+ case V4IR::Name::builtin_pop_scope:
+ callBuiltinPopScope();
+ return;
+
+ case V4IR::Name::builtin_declare_vars: {
+ if (!call->args)
+ return;
+ V4IR::Const *deletable = call->args->expr->asConst();
+ assert(deletable->type == V4IR::BoolType);
+ for (V4IR::ExprList *it = call->args->next; it; it = it->next) {
+ V4IR::Name *arg = it->expr->asName();
+ assert(arg != 0);
+ callBuiltinDeclareVar(deletable->value != 0, *arg->id);
+ }
+ } return;
+
+ case V4IR::Name::builtin_define_getter_setter: {
+ if (!call->args)
+ return;
+ V4IR::ExprList *args = call->args;
+ V4IR::Temp *object = args->expr->asTemp();
+ assert(object);
+ args = args->next;
+ assert(args);
+ V4IR::Name *name = args->expr->asName();
+ args = args->next;
+ assert(args);
+ V4IR::Temp *getter = args->expr->asTemp();
+ args = args->next;
+ assert(args);
+ V4IR::Temp *setter = args->expr->asTemp();
+
+ callBuiltinDefineGetterSetter(object, *name->id, getter, setter);
+ } return;
+
+ case V4IR::Name::builtin_define_property: {
+ if (!call->args)
+ return;
+ V4IR::ExprList *args = call->args;
+ V4IR::Temp *object = args->expr->asTemp();
+ assert(object);
+ args = args->next;
+ assert(args);
+ V4IR::Name *name = args->expr->asName();
+ args = args->next;
+ assert(args);
+ V4IR::Temp *value = args->expr->asTemp();
+
+ callBuiltinDefineProperty(object, *name->id, value);
+ } return;
+
+ case V4IR::Name::builtin_define_array:
+ callBuiltinDefineArray(result, call->args);
+ return;
+
+ case V4IR::Name::builtin_define_object_literal:
+ callBuiltinDefineObjectLiteral(result, call->args);
+ return;
+
+ default:
+ break;
+ }
+
+ Q_UNIMPLEMENTED();
+ call->dump(qout); qout << endl;
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
diff --git a/src/qml/qml/v4/qv4isel_p.h b/src/qml/qml/v4/qv4isel_p.h
new file mode 100644
index 0000000000..ca49b4c8b1
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ISEL_P_H
+#define QV4ISEL_P_H
+
+#include "qv4global_p.h"
+#include "qv4jsir_p.h"
+
+#include <qglobal.h>
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+struct ExecutionEngine;
+struct Function;
+}
+
+namespace QQmlJS {
+
+class Q_QML_EXPORT EvalInstructionSelection
+{
+public:
+ EvalInstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module);
+ virtual ~EvalInstructionSelection() = 0;
+
+ QV4::Function *vmFunction(V4IR::Function *f);
+
+ void setUseFastLookups(bool b) { useFastLookups = b; }
+
+protected:
+ QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction);
+ QV4::ExecutionEngine *engine() const { return _engine; }
+ virtual void run(QV4::Function *vmFunction, V4IR::Function *function) = 0;
+
+private:
+ QV4::ExecutionEngine *_engine;
+ QHash<V4IR::Function *, QV4::Function *> _irToVM;
+protected:
+ bool useFastLookups;
+};
+
+class Q_QML_EXPORT EvalISelFactory
+{
+public:
+ virtual ~EvalISelFactory() = 0;
+ virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) = 0;
+ virtual bool jitCompileRegexps() const = 0;
+};
+
+namespace V4IR {
+class Q_QML_EXPORT IRDecoder: protected V4IR::StmtVisitor
+{
+public:
+ virtual ~IRDecoder() = 0;
+
+ virtual void visitPhi(V4IR::Phi *) {}
+
+public: // visitor methods for StmtVisitor:
+ virtual void visitMove(V4IR::Move *s);
+ virtual void visitExp(V4IR::Exp *s);
+
+public: // to implement by subclasses:
+ virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0;
+ virtual void callBuiltinThrow(V4IR::Temp *arg) = 0;
+ virtual void callBuiltinFinishTry() = 0;
+ virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0;
+ virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0;
+ virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0;
+ virtual void callBuiltinPopScope() = 0;
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0;
+ virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0;
+ virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0;
+ virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0;
+ virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0;
+ virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void convertType(V4IR::Temp *source, V4IR::Temp *target) = 0;
+ virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0;
+ virtual void loadThisObject(V4IR::Temp *temp) = 0;
+ virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0;
+ virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0;
+ virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0;
+ virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0;
+ virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0;
+ virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0;
+ virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0;
+ virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0;
+ virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0;
+ virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0;
+ virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0;
+ virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0;
+ virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) = 0;
+ virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0;
+ virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0;
+ virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0;
+
+private:
+ void callBuiltin(V4IR::Call *c, V4IR::Temp *temp);
+};
+} // namespace IR
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ISEL_P_H
diff --git a/src/qml/qml/v4/qv4isel_util_p.h b/src/qml/qml/v4/qv4isel_util_p.h
new file mode 100644
index 0000000000..5aedaaff1b
--- /dev/null
+++ b/src/qml/qml/v4/qv4isel_util_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ISEL_UTIL_P_H
+#define QV4ISEL_UTIL_P_H
+
+#include "qv4runtime_p.h"
+#include "qv4jsir_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+inline QV4::Value convertToValue(V4IR::Const *c)
+{
+ switch (c->type) {
+ case V4IR::MissingType:
+ return QV4::Value::emptyValue();
+ case V4IR::NullType:
+ return QV4::Value::nullValue();
+ case V4IR::UndefinedType:
+ return QV4::Value::undefinedValue();
+ case V4IR::BoolType:
+ return QV4::Value::fromBoolean(c->value != 0);
+ case V4IR::SInt32Type:
+ return QV4::Value::fromInt32(int(c->value));
+ case V4IR::UInt32Type:
+ return QV4::Value::fromUInt32(unsigned(c->value));
+ case V4IR::DoubleType:
+ return QV4::Value::fromDouble(c->value);
+ case V4IR::NumberType: {
+ int ival = (int)c->value;
+ // +0 != -0, so we need to convert to double when negating 0
+ if (ival == c->value && !(c->value == 0 && isNegative(c->value))) {
+ return QV4::Value::fromInt32(ival);
+ } else {
+ return QV4::Value::fromDouble(c->value);
+ }
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ISEL_UTIL_P_H
diff --git a/src/qml/qml/v4/qv4jsir.cpp b/src/qml/qml/v4/qv4jsir.cpp
new file mode 100644
index 0000000000..7f8d257429
--- /dev/null
+++ b/src/qml/qml/v4/qv4jsir.cpp
@@ -0,0 +1,1024 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4jsir_p.h"
+#include <private/qqmljsast_p.h>
+
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qset.h>
+#include <cmath>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace V4IR {
+
+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 ObjectType: return QStringLiteral("object");
+ default: return QStringLiteral("multiple");
+ }
+}
+
+const char *opname(AluOp op)
+{
+ switch (op) {
+ case OpInvalid: return "?";
+
+ case OpIfTrue: return "(bool)";
+ case OpNot: return "!";
+ case OpUMinus: return "-";
+ case OpUPlus: return "+";
+ case OpCompl: return "~";
+ case OpIncrement: return "++";
+ case OpDecrement: return "--";
+
+ case OpBitAnd: return "&";
+ case OpBitOr: return "|";
+ case OpBitXor: return "^";
+
+ case OpAdd: return "+";
+ case OpSub: return "-";
+ case OpMul: return "*";
+ case OpDiv: return "/";
+ case OpMod: return "%";
+
+ case OpLShift: return "<<";
+ case OpRShift: return ">>";
+ case OpURShift: return ">>>";
+
+ case OpGt: return ">";
+ case OpLt: return "<";
+ case OpGe: return ">=";
+ case OpLe: return "<=";
+ case OpEqual: return "==";
+ case OpNotEqual: return "!=";
+ case OpStrictEqual: return "===";
+ case OpStrictNotEqual: return "!==";
+
+ case OpInstanceof: return "instanceof";
+ case OpIn: return "in";
+
+ case OpAnd: return "&&";
+ case OpOr: return "||";
+
+ 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;
+ }
+}
+
+struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor
+{
+ CloneExpr clone;
+ QSet<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function
+ Expr *uniqueExpr;
+
+ RemoveSharedExpressions(): uniqueExpr(0) {}
+
+ void operator()(V4IR::Function *function)
+ {
+ subexpressions.clear();
+
+ foreach (BasicBlock *block, function->basicBlocks) {
+ clone.setBasicBlock(block);
+
+ foreach (Stmt *s, block->statements) {
+ s->accept(this);
+ }
+ }
+ }
+
+ template <typename _Expr>
+ _Expr *cleanup(_Expr *expr)
+ {
+ if (subexpressions.contains(expr)) {
+ // the cloned expression is unique by definition
+ // so we don't need to add it to `subexpressions'.
+ return clone(expr);
+ }
+
+ subexpressions.insert(expr);
+ V4IR::Expr *e = expr;
+ qSwap(uniqueExpr, e);
+ expr->accept(this);
+ qSwap(uniqueExpr, e);
+ return static_cast<_Expr *>(e);
+ }
+
+ // statements
+ virtual void visitExp(Exp *s)
+ {
+ s->expr = cleanup(s->expr);
+ }
+
+ virtual void visitMove(Move *s)
+ {
+ s->target = cleanup(s->target);
+ s->source = cleanup(s->source);
+ }
+
+ virtual void visitJump(Jump *)
+ {
+ // nothing to do for Jump statements
+ }
+
+ virtual void visitCJump(CJump *s)
+ {
+ s->cond = cleanup(s->cond);
+ }
+
+ virtual void visitRet(Ret *s)
+ {
+ s->expr = cleanup(s->expr);
+ }
+
+ virtual void visitTry(Try *)
+ {
+ // nothing to do for Try statements
+ }
+
+ virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); abort(); }
+
+ // expressions
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitTemp(Temp *) {}
+ virtual void visitClosure(Closure *) {}
+
+ virtual void visitConvert(Convert *e)
+ {
+ e->expr = cleanup(e->expr);
+ }
+
+ virtual void visitUnop(Unop *e)
+ {
+ e->expr = cleanup(e->expr);
+ }
+
+ virtual void visitBinop(Binop *e)
+ {
+ e->left = cleanup(e->left);
+ e->right = cleanup(e->right);
+ }
+
+ virtual void visitCall(Call *e)
+ {
+ e->base = cleanup(e->base);
+ for (V4IR::ExprList *it = e->args; it; it = it->next)
+ it->expr = cleanup(it->expr);
+ }
+
+ virtual void visitNew(New *e)
+ {
+ e->base = cleanup(e->base);
+ for (V4IR::ExprList *it = e->args; it; it = it->next)
+ it->expr = cleanup(it->expr);
+ }
+
+ virtual void visitSubscript(Subscript *e)
+ {
+ e->base = cleanup(e->base);
+ e->index = cleanup(e->index);
+ }
+
+ virtual void visitMember(Member *e)
+ {
+ e->base = cleanup(e->base);
+ }
+};
+
+static QString dumpStart(const Expr *e) {
+ if (e->type == UnknownType)
+// return QStringLiteral("**UNKNOWN**");
+ return QString();
+ else
+ return typeName(e->type) + QStringLiteral("{");
+}
+
+static const char *dumpEnd(const Expr *e) {
+ if (e->type == UnknownType)
+ return "";
+ else
+ return "}";
+}
+
+void Const::dump(QTextStream &out) const
+{
+ if (type != UndefinedType && type != NullType)
+ out << dumpStart(this);
+ switch (type) {
+ case QQmlJS::V4IR::UndefinedType:
+ out << "undefined";
+ break;
+ case QQmlJS::V4IR::NullType:
+ out << "null";
+ break;
+ case QQmlJS::V4IR::BoolType:
+ out << (value ? "true" : "false");
+ break;
+ case QQmlJS::V4IR::MissingType:
+ out << "missing";
+ break;
+ default:
+ out << QString::number(value, 'g', 16);
+ break;
+ }
+ if (type != UndefinedType && type != NullType)
+ out << dumpEnd(this);
+}
+
+void String::dump(QTextStream &out) const
+{
+ out << '"' << escape(*value) << '"';
+}
+
+QString String::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 += QStringLiteral("\\n");
+ else if (ch == QLatin1Char('\r'))
+ r += QStringLiteral("\\r");
+ else if (ch == QLatin1Char('\\'))
+ r += QStringLiteral("\\\\");
+ else if (ch == QLatin1Char('"'))
+ r += QStringLiteral("\\\"");
+ else if (ch == QLatin1Char('\''))
+ r += QStringLiteral("\\'");
+ else
+ r += ch;
+ }
+ return r;
+}
+
+void RegExp::dump(QTextStream &out) const
+{
+ char f[3];
+ int i = 0;
+ if (flags & RegExp_Global)
+ f[i++] = 'g';
+ if (flags & RegExp_IgnoreCase)
+ f[i++] = 'i';
+ if (flags & RegExp_Multiline)
+ f[i++] = 'm';
+ f[i] = 0;
+
+ out << '/' << *value << '/' << f;
+}
+
+void Name::initGlobal(const QString *id, quint32 line, quint32 column)
+{
+ this->id = id;
+ this->builtin = builtin_invalid;
+ this->global = true;
+ 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->line = line;
+ this->column = column;
+}
+
+void Name::init(Builtin builtin, quint32 line, quint32 column)
+{
+ this->id = 0;
+ this->builtin = builtin;
+ this->global = false;
+ this->line = line;
+ this->column = column;
+}
+
+static 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_postincrement:
+ return "builtin_postincrement";
+ case Name::builtin_postdecrement:
+ return "builtin_postdecrement";
+ case Name::builtin_throw:
+ return "builtin_throw";
+ case Name::builtin_finish_try:
+ return "builtin_finish_try";
+ case V4IR::Name::builtin_foreach_iterator_object:
+ return "builtin_foreach_iterator_object";
+ case V4IR::Name::builtin_foreach_next_property_name:
+ return "builtin_foreach_next_property_name";
+ case V4IR::Name::builtin_push_with_scope:
+ return "builtin_push_with_scope";
+ case V4IR::Name::builtin_pop_scope:
+ return "builtin_pop_scope";
+ case V4IR::Name::builtin_declare_vars:
+ return "builtin_declare_vars";
+ case V4IR::Name::builtin_define_property:
+ return "builtin_define_property";
+ case V4IR::Name::builtin_define_array:
+ return "builtin_define_array";
+ case V4IR::Name::builtin_define_getter_setter:
+ return "builtin_define_getter_setter";
+ case V4IR::Name::builtin_define_object_literal:
+ return "builtin_define_object_literal";
+ }
+ return "builtin_(###FIXME)";
+};
+
+void Name::dump(QTextStream &out) const
+{
+ if (id)
+ out << *id;
+ else
+ out << builtin_to_string(builtin);
+}
+
+void Temp::dump(QTextStream &out) const
+{
+ out << dumpStart(this);
+ switch (kind) {
+ case Formal: out << '#' << index; break;
+ case ScopedFormal: out << '#' << index
+ << '@' << scope; break;
+ case Local: out << '$' << index; break;
+ case ScopedLocal: out << '$' << index
+ << '@' << scope; break;
+ case VirtualRegister: out << '%' << index; break;
+ default: out << "INVALID";
+ }
+ out << dumpEnd(this);
+}
+
+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;
+ if (t1.index < t2.index) return true;
+ if (t1.index > t2.index) return false;
+ return t1.scope < t2.scope;
+}
+
+void Closure::dump(QTextStream &out) const
+{
+ QString name = value->name ? *value->name : QString();
+ if (name.isEmpty())
+ name.sprintf("%p", value);
+ out << "closure(" << name << ')';
+}
+
+void Convert::dump(QTextStream &out) const
+{
+ out << dumpStart(this);
+ out << "convert(";
+ expr->dump(out);
+ out << ')' << dumpEnd(this);
+}
+
+void Unop::dump(QTextStream &out) const
+{
+ out << dumpStart(this) << opname(op);
+ expr->dump(out);
+ out << dumpEnd(this);
+}
+
+void Binop::dump(QTextStream &out) const
+{
+ out << dumpStart(this);
+ left->dump(out);
+ out << ' ' << opname(op) << ' ';
+ right->dump(out);
+ out << dumpEnd(this);
+}
+
+void Call::dump(QTextStream &out) const
+{
+ base->dump(out);
+ out << '(';
+ for (ExprList *it = args; it; it = it->next) {
+ if (it != args)
+ out << ", ";
+ it->expr->dump(out);
+ }
+ out << ')';
+}
+
+void New::dump(QTextStream &out) const
+{
+ out << "new ";
+ base->dump(out);
+ out << '(';
+ for (ExprList *it = args; it; it = it->next) {
+ if (it != args)
+ out << ", ";
+ it->expr->dump(out);
+ }
+ out << ')';
+}
+
+void Subscript::dump(QTextStream &out) const
+{
+ base->dump(out);
+ out << '[';
+ index->dump(out);
+ out << ']';
+}
+
+void Member::dump(QTextStream &out) const
+{
+ base->dump(out);
+ out << '.' << *name;
+}
+
+void Exp::dump(QTextStream &out, Mode)
+{
+ out << "(void) ";
+ expr->dump(out);
+ out << ';';
+}
+
+void Move::dump(QTextStream &out, Mode)
+{
+ target->dump(out);
+ out << ' ';
+ if (op != OpInvalid)
+ out << opname(op);
+ out << "= ";
+// if (source->type != target->type)
+// out << typeName(source->type) << "_to_" << typeName(target->type) << '(';
+ source->dump(out);
+// if (source->type != target->type)
+// out << ')';
+ out << ';';
+}
+
+void Jump::dump(QTextStream &out, Mode mode)
+{
+ Q_UNUSED(mode);
+ out << "goto " << 'L' << target->index << ';';
+}
+
+void CJump::dump(QTextStream &out, Mode mode)
+{
+ Q_UNUSED(mode);
+ out << "if (";
+ cond->dump(out);
+ if (mode == HIR)
+ out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';';
+ else
+ out << ") goto " << 'L' << iftrue->index << ";";
+}
+
+void Ret::dump(QTextStream &out, Mode)
+{
+ out << "return";
+ if (expr) {
+ out << ' ';
+ expr->dump(out);
+ }
+ out << ';';
+}
+
+void Try::dump(QTextStream &out, Stmt::Mode mode)
+{
+ out << "try L" << tryBlock->index << "; catch exception in ";
+ exceptionVar->dump(out);
+ out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';';
+}
+
+void Phi::dump(QTextStream &out, Stmt::Mode mode)
+{
+ targetTemp->dump(out);
+ out << " = phi(";
+ for (int i = 0, ei = incoming.size(); i < ei; ++i) {
+ if (i > 0)
+ out << ", ";
+ if (incoming[i])
+ incoming[i]->dump(out);
+ }
+ out << ");";
+}
+
+Function *Module::newFunction(const QString &name, Function *outer)
+{
+ Function *f = new Function(this, outer, name);
+ functions.append(f);
+ if (!outer) {
+ assert(!rootFunction);
+ rootFunction = f;
+ } else {
+ outer->nestedFunctions.append(f);
+ }
+ return f;
+}
+
+Module::~Module()
+{
+ foreach (Function *f, functions) {
+ delete f;
+ }
+}
+
+Function::~Function()
+{
+ // destroy the Stmt::Data blocks manually, because memory pool cleanup won't
+ // call the Stmt destructors.
+ foreach (V4IR::BasicBlock *b, basicBlocks)
+ foreach (V4IR::Stmt *s, b->statements)
+ s->destroyData();
+
+ qDeleteAll(basicBlocks);
+ pool = 0;
+ module = 0;
+}
+
+
+const QString *Function::newString(const QString &text)
+{
+ return &*strings.insert(text);
+}
+
+BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode)
+{
+ BasicBlock *block = new BasicBlock(this, containingLoop);
+ return mode == InsertBlock ? insertBasicBlock(block) : block;
+}
+
+void Function::dump(QTextStream &out, Stmt::Mode mode)
+{
+ QString n = name ? *name : QString();
+ if (n.isEmpty())
+ n.sprintf("%p", this);
+ out << "function " << n << "() {" << endl;
+ foreach (const QString *formal, formals)
+ out << "\treceive " << *formal << ';' << endl;
+ foreach (const QString *local, locals)
+ out << "\tlocal " << *local << ';' << endl;
+ foreach (BasicBlock *bb, basicBlocks)
+ bb->dump(out, mode);
+ out << '}' << endl;
+}
+
+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;
+}
+unsigned BasicBlock::newTemp()
+{
+ return function->tempCount++;
+}
+
+Temp *BasicBlock::TEMP(unsigned index)
+{
+ Temp *e = function->New<Temp>();
+ e->init(Temp::VirtualRegister, index, 0);
+ return e;
+}
+
+Temp *BasicBlock::ARG(unsigned index, unsigned scope)
+{
+ Temp *e = function->New<Temp>();
+ e->init(scope ? Temp::ScopedFormal : Temp::Formal, index, scope);
+ return e;
+}
+
+Temp *BasicBlock::LOCAL(unsigned index, unsigned scope)
+{
+ Temp *e = function->New<Temp>();
+ e->init(scope ? Temp::ScopedLocal : Temp::Local, index, scope);
+ return e;
+}
+
+Expr *BasicBlock::CONST(Type type, double value)
+{
+ 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;
+ }
+ e->init(type, value);
+ return e;
+}
+
+Expr *BasicBlock::STRING(const QString *value)
+{
+ String *e = function->New<String>();
+ e->init(value);
+ return e;
+}
+
+Expr *BasicBlock::REGEXP(const QString *value, int flags)
+{
+ RegExp *e = function->New<RegExp>();
+ e->init(value, flags);
+ return e;
+}
+
+Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
+{
+ Name *e = function->New<Name>();
+ e->init(function->newString(id), line, column);
+ return e;
+}
+
+Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column)
+{
+ Name *e = function->New<Name>();
+ e->initGlobal(function->newString(id), line, column);
+ return e;
+}
+
+
+Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
+{
+ Name *e = function->New<Name>();
+ e->init(builtin, line, column);
+ return e;
+}
+
+Closure *BasicBlock::CLOSURE(Function *function)
+{
+ Closure *clos = function->New<Closure>();
+ clos->init(function);
+ return clos;
+}
+
+Expr *BasicBlock::CONVERT(Expr *expr, Type type)
+{
+ Convert *e = function->New<Convert>();
+ e->init(expr, type);
+ return e;
+}
+
+Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
+{
+ Unop *e = function->New<Unop>();
+ e->init(op, expr);
+ return e;
+}
+
+Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
+{
+ Binop *e = function->New<Binop>();
+ e->init(op, left, right);
+ return e;
+}
+
+Expr *BasicBlock::CALL(Expr *base, ExprList *args)
+{
+ 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;
+}
+
+Expr *BasicBlock::NEW(Expr *base, ExprList *args)
+{
+ New *e = function->New<New>();
+ e->init(base, args);
+ return e;
+}
+
+Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
+{
+ Subscript *e = function->New<Subscript>();
+ e->init(base, index);
+ return e;
+}
+
+Expr *BasicBlock::MEMBER(Expr *base, const QString *name)
+{
+ Member*e = function->New<Member>();
+ e->init(base, name);
+ return e;
+}
+
+Stmt *BasicBlock::EXP(Expr *expr)
+{
+ if (isTerminated())
+ return 0;
+
+ Exp *s = function->New<Exp>();
+ s->init(expr);
+ appendStatement(s);
+ return s;
+}
+
+Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op)
+{
+ if (isTerminated())
+ return 0;
+
+ Move *s = function->New<Move>();
+ s->init(target, source, op);
+ appendStatement(s);
+ return s;
+}
+
+Stmt *BasicBlock::JUMP(BasicBlock *target)
+{
+ if (isTerminated())
+ return 0;
+
+ Jump *s = function->New<Jump>();
+ s->init(target);
+ appendStatement(s);
+
+ assert(! out.contains(target));
+ out.append(target);
+
+ assert(! target->in.contains(this));
+ target->in.append(this);
+
+ return s;
+}
+
+Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
+{
+ if (isTerminated())
+ return 0;
+
+ if (iftrue == iffalse) {
+ MOVE(TEMP(newTemp()), cond);
+ return JUMP(iftrue);
+ }
+
+ CJump *s = function->New<CJump>();
+ s->init(cond, iftrue, iffalse);
+ appendStatement(s);
+
+ assert(! out.contains(iftrue));
+ out.append(iftrue);
+
+ assert(! iftrue->in.contains(this));
+ iftrue->in.append(this);
+
+ assert(! out.contains(iffalse));
+ out.append(iffalse);
+
+ assert(! iffalse->in.contains(this));
+ iffalse->in.append(this);
+
+ return s;
+}
+
+Stmt *BasicBlock::RET(Temp *expr)
+{
+ if (isTerminated())
+ return 0;
+
+ Ret *s = function->New<Ret>();
+ s->init(expr);
+ appendStatement(s);
+ return s;
+}
+
+Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar)
+{
+ if (isTerminated())
+ return 0;
+
+ Try *t = function->New<Try>();
+ t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar);
+ appendStatement(t);
+
+ assert(! out.contains(tryBlock));
+ out.append(tryBlock);
+
+ assert(! out.contains(catchBlock));
+ out.append(catchBlock);
+
+ assert(! tryBlock->in.contains(this));
+ tryBlock->in.append(this);
+
+ assert(! catchBlock->in.contains(this));
+ catchBlock->in.append(this);
+
+ return t;
+}
+
+void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
+{
+ out << 'L' << index << ':' << endl;
+ foreach (Stmt *s, statements) {
+ out << '\t';
+ s->dump(out, mode);
+
+ if (s->location.isValid())
+ out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn;
+
+ out << endl;
+ }
+}
+
+void BasicBlock::appendStatement(Stmt *statement)
+{
+ if (nextLocation.isValid()) {
+ statement->location = nextLocation;
+ nextLocation = AST::SourceLocation();
+ }
+ statements.append(statement);
+}
+
+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<V4IR::ExprList>();
+ clonedList->init(clone(list->expr), clone(list->next));
+ return clonedList;
+}
+
+void CloneExpr::visitConst(Const *e)
+{
+ cloned = block->CONST(e->type, e->value);
+}
+
+void CloneExpr::visitString(String *e)
+{
+ cloned = block->STRING(e->value);
+}
+
+void CloneExpr::visitRegExp(RegExp *e)
+{
+ cloned = block->REGEXP(e->value, e->flags);
+}
+
+void CloneExpr::visitName(Name *e)
+{
+ if (e->id)
+ cloned = block->NAME(*e->id, e->line, e->column);
+ else
+ cloned = block->NAME(e->builtin, e->line, e->column);
+}
+
+void CloneExpr::visitTemp(Temp *e)
+{
+ Temp *t = block->function->New<Temp>();
+ t->init(e->kind, e->index, e->scope);
+ cloned = t;
+}
+
+void CloneExpr::visitClosure(Closure *e)
+{
+ cloned = block->CLOSURE(e->value);
+}
+
+void CloneExpr::visitConvert(Convert *e)
+{
+ cloned = block->CONVERT(clone(e->expr), e->type);
+}
+
+void CloneExpr::visitUnop(Unop *e)
+{
+ cloned = block->UNOP(e->op, clone(e->expr));
+}
+
+void CloneExpr::visitBinop(Binop *e)
+{
+ cloned = block->BINOP(e->op, clone(e->left), clone(e->right));
+}
+
+void CloneExpr::visitCall(Call *e)
+{
+ cloned = block->CALL(clone(e->base), clone(e->args));
+}
+
+void CloneExpr::visitNew(New *e)
+{
+ cloned = block->NEW(clone(e->base), clone(e->args));
+}
+
+void CloneExpr::visitSubscript(Subscript *e)
+{
+ cloned = block->SUBSCRIPT(clone(e->base), clone(e->index));
+}
+
+void CloneExpr::visitMember(Member *e)
+{
+ cloned = block->MEMBER(clone(e->base), e->name);
+}
+
+} // end of namespace IR
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4jsir_p.h b/src/qml/qml/v4/qv4jsir_p.h
new file mode 100644
index 0000000000..659d9870b1
--- /dev/null
+++ b/src/qml/qml/v4/qv4jsir_p.h
@@ -0,0 +1,890 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.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 "qv4global_p.h"
+#include <private/qqmljsmemorypool_p.h>
+#include <private/qqmljsastfwd_p.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QString>
+#include <QtCore/QBitArray>
+#include <QtCore/qurl.h>
+
+#ifdef CONST
+#undef CONST
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+class QQmlType;
+
+namespace QV4 {
+struct ExecutionContext;
+}
+
+namespace QQmlJS {
+
+inline bool isNegative(double d)
+{
+ uchar *dch = (uchar *)&d;
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ return (dch[0] & 0x80);
+ else
+ return (dch[7] & 0x80);
+
+}
+
+namespace V4IR {
+
+struct BasicBlock;
+struct Function;
+struct Module;
+
+struct Stmt;
+struct Expr;
+
+// expressions
+struct Const;
+struct String;
+struct RegExp;
+struct Name;
+struct Temp;
+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 Try;
+struct Phi;
+
+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(V4IR::AluOp op);
+
+enum Type {
+ 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,
+ ObjectType = 1 << 8
+};
+QString typeName(Type t);
+
+struct ExprVisitor {
+ virtual ~ExprVisitor() {}
+ virtual void visitConst(Const *) = 0;
+ virtual void visitString(String *) = 0;
+ virtual void visitRegExp(RegExp *) = 0;
+ virtual void visitName(Name *) = 0;
+ virtual void visitTemp(Temp *) = 0;
+ virtual void visitClosure(Closure *) = 0;
+ virtual void visitConvert(Convert *) = 0;
+ virtual void visitUnop(Unop *) = 0;
+ virtual void visitBinop(Binop *) = 0;
+ virtual void visitCall(Call *) = 0;
+ virtual void visitNew(New *) = 0;
+ virtual void visitSubscript(Subscript *) = 0;
+ virtual void visitMember(Member *) = 0;
+};
+
+struct StmtVisitor {
+ virtual ~StmtVisitor() {}
+ virtual void visitExp(Exp *) = 0;
+ virtual void visitMove(Move *) = 0;
+ virtual void visitJump(Jump *) = 0;
+ virtual void visitCJump(CJump *) = 0;
+ virtual void visitRet(Ret *) = 0;
+ virtual void visitTry(Try *) = 0;
+ virtual void visitPhi(Phi *) = 0;
+};
+
+struct Expr {
+ Type type;
+
+ Expr(): type(UnknownType) {}
+ virtual ~Expr() {}
+ virtual void accept(ExprVisitor *) = 0;
+ virtual bool isLValue() { return false; }
+ virtual Const *asConst() { return 0; }
+ virtual String *asString() { return 0; }
+ virtual RegExp *asRegExp() { return 0; }
+ virtual Name *asName() { return 0; }
+ virtual Temp *asTemp() { return 0; }
+ virtual Closure *asClosure() { return 0; }
+ virtual Convert *asConvert() { return 0; }
+ virtual Unop *asUnop() { return 0; }
+ virtual Binop *asBinop() { return 0; }
+ virtual Call *asCall() { return 0; }
+ virtual New *asNew() { return 0; }
+ virtual Subscript *asSubscript() { return 0; }
+ virtual Member *asMember() { return 0; }
+ virtual void dump(QTextStream &out) const = 0;
+};
+
+struct ExprList {
+ Expr *expr;
+ ExprList *next;
+
+ void init(Expr *expr, ExprList *next = 0)
+ {
+ this->expr = expr;
+ this->next = next;
+ }
+};
+
+struct Const: Expr {
+ double value;
+
+ void init(Type type, double value)
+ {
+ this->type = type;
+ this->value = value;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitConst(this); }
+ virtual Const *asConst() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct String: Expr {
+ const QString *value;
+
+ void init(const QString *value)
+ {
+ this->value = value;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitString(this); }
+ virtual String *asString() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+ static QString escape(const QString &s);
+};
+
+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;
+
+ void init(const QString *value, int flags)
+ {
+ this->value = value;
+ this->flags = flags;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitRegExp(this); }
+ virtual RegExp *asRegExp() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Name: Expr {
+ enum Builtin {
+ builtin_invalid,
+ builtin_typeof,
+ builtin_delete,
+ builtin_postincrement,
+ builtin_postdecrement,
+ builtin_throw,
+ builtin_finish_try,
+ builtin_foreach_iterator_object,
+ builtin_foreach_next_property_name,
+ builtin_push_with_scope,
+ builtin_pop_scope,
+ builtin_declare_vars,
+ builtin_define_property,
+ builtin_define_array,
+ builtin_define_getter_setter,
+ builtin_define_object_literal
+ };
+
+ const QString *id;
+ Builtin builtin;
+ bool global;
+ quint32 line;
+ quint32 column;
+
+ 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);
+
+ virtual void accept(ExprVisitor *v) { v->visitName(this); }
+ virtual bool isLValue() { return true; }
+ virtual Name *asName() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Temp: Expr {
+ enum Kind {
+ Formal = 0,
+ ScopedFormal,
+ Local,
+ ScopedLocal,
+ VirtualRegister,
+ PhysicalRegister,
+ StackSlot
+ };
+
+ unsigned index;
+ unsigned scope : 29; // how many scopes outside the current one?
+ unsigned kind : 3;
+
+ 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;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitTemp(this); }
+ virtual bool isLValue() { return true; }
+ virtual Temp *asTemp() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
+{ return t1.index == t2.index && t1.scope == t2.scope && t1.kind == t2.kind; }
+
+inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW
+{ return t.index ^ (t.kind | (t.scope << 3)) ^ seed; }
+
+bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW;
+
+struct Closure: Expr {
+ Function *value;
+
+ void init(Function *value)
+ {
+ this->value = value;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitClosure(this); }
+ virtual Closure *asClosure() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Convert: Expr {
+ Expr *expr;
+
+ void init(Expr *expr, Type type)
+ {
+ this->expr = expr;
+ this->type = type;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitConvert(this); }
+ virtual Convert *asConvert() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Unop: Expr {
+ AluOp op;
+ Expr *expr;
+
+ void init(AluOp op, Expr *expr)
+ {
+ this->op = op;
+ this->expr = expr;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitUnop(this); }
+ virtual Unop *asUnop() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Binop: Expr {
+ AluOp op;
+ Expr *left; // Temp or Const
+ Expr *right; // Temp or Const
+
+ void init(AluOp op, Expr *left, Expr *right)
+ {
+ this->op = op;
+ this->left = left;
+ this->right = right;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitBinop(this); }
+ virtual Binop *asBinop() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Call: Expr {
+ Expr *base; // Name, Member, Temp
+ ExprList *args; // List of Temps
+
+ void init(Expr *base, ExprList *args)
+ {
+ this->base = base;
+ this->args = args;
+ }
+
+ Expr *onlyArgument() const {
+ if (args && ! args->next)
+ return args->expr;
+ return 0;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitCall(this); }
+ virtual Call *asCall() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct New: Expr {
+ Expr *base; // Name, Member, Temp
+ ExprList *args; // List of Temps
+
+ void init(Expr *base, ExprList *args)
+ {
+ this->base = base;
+ this->args = args;
+ }
+
+ Expr *onlyArgument() const {
+ if (args && ! args->next)
+ return args->expr;
+ return 0;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitNew(this); }
+ virtual New *asNew() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Subscript: Expr {
+ Expr *base;
+ Expr *index;
+
+ void init(Expr *base, Expr *index)
+ {
+ this->base = base;
+ this->index = index;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitSubscript(this); }
+ virtual bool isLValue() { return true; }
+ virtual Subscript *asSubscript() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Member: Expr {
+ Expr *base;
+ const QString *name;
+
+ void init(Expr *base, const QString *name)
+ {
+ this->base = base;
+ this->name = name;
+ }
+
+ virtual void accept(ExprVisitor *v) { v->visitMember(this); }
+ virtual bool isLValue() { return true; }
+ virtual Member *asMember() { return this; }
+
+ virtual void dump(QTextStream &out) const;
+};
+
+struct Stmt {
+ enum Mode {
+ HIR,
+ MIR
+ };
+
+ struct Data {
+ QVector<unsigned> uses;
+ QVector<unsigned> defs;
+ QBitArray liveIn;
+ QBitArray liveOut;
+ };
+
+ Data *d;
+ int id;
+ AST::SourceLocation location;
+
+ Stmt(): d(0), id(-1) {}
+ virtual ~Stmt() { Q_UNREACHABLE(); }
+ virtual Stmt *asTerminator() { return 0; }
+
+ virtual void accept(StmtVisitor *) = 0;
+ virtual Exp *asExp() { return 0; }
+ virtual Move *asMove() { return 0; }
+ virtual Jump *asJump() { return 0; }
+ virtual CJump *asCJump() { return 0; }
+ virtual Ret *asRet() { return 0; }
+ virtual Try *asTry() { return 0; }
+ virtual Phi *asPhi() { return 0; }
+ virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
+
+ void destroyData() {
+ delete d;
+ d = 0;
+ }
+};
+
+struct Exp: Stmt {
+ Expr *expr;
+
+ void init(Expr *expr)
+ {
+ this->expr = expr;
+ }
+
+ virtual void accept(StmtVisitor *v) { v->visitExp(this); }
+ virtual Exp *asExp() { return this; }
+
+ virtual void dump(QTextStream &out, Mode);
+};
+
+struct Move: Stmt {
+ Expr *target; // LHS - Temp, Name, Member or Subscript
+ Expr *source;
+ AluOp op;
+
+ void init(Expr *target, Expr *source, AluOp op)
+ {
+ this->target = target;
+ this->source = source;
+ this->op = op;
+ }
+
+ virtual void accept(StmtVisitor *v) { v->visitMove(this); }
+ virtual Move *asMove() { return this; }
+
+ virtual void dump(QTextStream &out, Mode);
+};
+
+struct Jump: Stmt {
+ BasicBlock *target;
+
+ void init(BasicBlock *target)
+ {
+ this->target = target;
+ }
+
+ virtual Stmt *asTerminator() { return this; }
+
+ virtual void accept(StmtVisitor *v) { v->visitJump(this); }
+ virtual Jump *asJump() { return this; }
+
+ virtual void dump(QTextStream &out, Mode mode);
+};
+
+struct CJump: Stmt {
+ Expr *cond; // Temp, Binop
+ BasicBlock *iftrue;
+ BasicBlock *iffalse;
+
+ void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
+ {
+ this->cond = cond;
+ this->iftrue = iftrue;
+ this->iffalse = iffalse;
+ }
+
+ virtual Stmt *asTerminator() { return this; }
+
+ virtual void accept(StmtVisitor *v) { v->visitCJump(this); }
+ virtual CJump *asCJump() { return this; }
+
+ virtual void dump(QTextStream &out, Mode mode);
+};
+
+struct Ret: Stmt {
+ Expr *expr;
+
+ void init(Expr *expr)
+ {
+ this->expr = expr;
+ }
+
+ virtual Stmt *asTerminator() { return this; }
+
+ virtual void accept(StmtVisitor *v) { v->visitRet(this); }
+ virtual Ret *asRet() { return this; }
+
+ virtual void dump(QTextStream &out, Mode);
+};
+
+struct Try: Stmt {
+ BasicBlock *tryBlock;
+ BasicBlock *catchBlock;
+ QString exceptionVarName;
+ Temp *exceptionVar; // place to store the caught exception, for use when re-throwing
+
+ void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar)
+ {
+ this->tryBlock = tryBlock;
+ this->catchBlock = catchBlock;
+ this->exceptionVarName = exceptionVarName;
+ this->exceptionVar = exceptionVar;
+ }
+
+ virtual Stmt *asTerminator() { return this; }
+
+ virtual void accept(StmtVisitor *v) { v->visitTry(this); }
+ virtual Try *asTry() { return this; }
+
+ virtual void dump(QTextStream &out, Mode mode);
+};
+
+struct Phi: Stmt {
+ Temp *targetTemp;
+ QVector<Expr *> incoming;
+
+ virtual void accept(StmtVisitor *v) { v->visitPhi(this); }
+ virtual Phi *asPhi() { return this; }
+
+ virtual void dump(QTextStream &out, Mode mode);
+};
+
+struct Q_QML_EXPORT Module {
+ MemoryPool pool;
+ QVector<Function *> functions;
+ Function *rootFunction;
+
+ Function *newFunction(const QString &name, Function *outer);
+
+ Module() : rootFunction(0) {}
+ ~Module();
+};
+
+struct Function {
+ Module *module;
+ MemoryPool *pool;
+ const QString *name;
+ QVector<BasicBlock *> basicBlocks;
+ int tempCount;
+ int maxNumberOfArguments;
+ QSet<QString> strings;
+ QList<const QString *> formals;
+ QList<const QString *> locals;
+ QVector<Function *> nestedFunctions;
+ Function *outer;
+
+ QString sourceFile;
+
+ int insideWithOrCatch;
+
+ uint hasDirectEval: 1;
+ uint usesArgumentsObject : 1;
+ uint isStrict: 1;
+ uint isNamedExpression : 1;
+ uint hasTry: 1;
+ uint hasWith: 1;
+ uint unused : 26;
+
+ template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
+
+ 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)
+ , isStrict(false)
+ , isNamedExpression(false)
+ , hasTry(false)
+ , hasWith(false)
+ , unused(0)
+ { this->name = newString(name); }
+
+ ~Function();
+
+ enum BasicBlockInsertMode {
+ InsertBlock,
+ DontInsertBlock
+ };
+
+ BasicBlock *newBasicBlock(BasicBlock *containingLoop, 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)); }
+
+ inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; }
+
+ void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
+
+ void removeSharedExpressions();
+
+ int indexOfArgument(const QStringRef &string) const;
+
+ bool variablesCanEscape() const
+ { return hasDirectEval || !nestedFunctions.isEmpty(); }
+};
+
+struct BasicBlock {
+ Function *function;
+ QVector<Stmt *> statements;
+ QVector<BasicBlock *> in;
+ QVector<BasicBlock *> out;
+ QBitArray liveIn;
+ QBitArray liveOut;
+ int index;
+ AST::SourceLocation nextLocation;
+
+ BasicBlock(Function *function, BasicBlock *containingLoop)
+ : function(function)
+ , index(-1)
+ , _containingGroup(containingLoop)
+ , _groupStart(false)
+ {}
+ ~BasicBlock() {}
+
+ template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; }
+
+ inline bool isEmpty() const {
+ return statements.isEmpty();
+ }
+
+ inline Stmt *terminator() const {
+ if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0)
+ return statements.at(statements.size() - 1);
+ return 0;
+ }
+
+ inline bool isTerminated() const {
+ if (terminator() != 0)
+ return true;
+ return false;
+ }
+
+ unsigned newTemp();
+
+ Temp *TEMP(unsigned kind);
+ Temp *ARG(unsigned index, unsigned scope);
+ Temp *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(Function *function);
+
+ 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);
+
+ Stmt *EXP(Expr *expr);
+
+ Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid);
+
+ Stmt *JUMP(BasicBlock *target);
+ Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
+ Stmt *RET(Temp *expr);
+ Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar);
+
+ void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
+
+ void appendStatement(Stmt *statement);
+
+ BasicBlock *containingGroup() const
+ { return _containingGroup; }
+
+ bool isGroupStart() const
+ { return _groupStart; }
+
+ void markAsGroupStart()
+ { _groupStart = true; }
+
+private:
+ BasicBlock *_containingGroup;
+ bool _groupStart;
+};
+
+class CloneExpr: protected V4IR::ExprVisitor
+{
+public:
+ explicit CloneExpr(V4IR::BasicBlock *block = 0);
+
+ void setBasicBlock(V4IR::BasicBlock *block);
+
+ template <typename _Expr>
+ _Expr *operator()(_Expr *expr)
+ {
+ return clone(expr);
+ }
+
+ template <typename _Expr>
+ _Expr *clone(_Expr *expr)
+ {
+ Expr *c = expr;
+ qSwap(cloned, c);
+ expr->accept(this);
+ qSwap(cloned, c);
+ return static_cast<_Expr *>(c);
+ }
+
+protected:
+ V4IR::ExprList *clone(V4IR::ExprList *list);
+
+ virtual void visitConst(Const *);
+ virtual void visitString(String *);
+ virtual void visitRegExp(RegExp *);
+ virtual void visitName(Name *);
+ virtual void visitTemp(Temp *);
+ virtual void visitClosure(Closure *);
+ virtual void visitConvert(Convert *);
+ virtual void visitUnop(Unop *);
+ virtual void visitBinop(Binop *);
+ virtual void visitCall(Call *);
+ virtual void visitNew(New *);
+ virtual void visitSubscript(Subscript *);
+ virtual void visitMember(Member *);
+
+private:
+ V4IR::BasicBlock *block;
+ V4IR::Expr *cloned;
+};
+
+} // end of namespace IR
+
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4IR_P_H
diff --git a/src/qml/qml/v4/qv4jsonobject.cpp b/src/qml/qml/v4/qv4jsonobject.cpp
new file mode 100644
index 0000000000..782c388e5a
--- /dev/null
+++ b/src/qml/qml/v4/qv4jsonobject.cpp
@@ -0,0 +1,1040 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4jsonobject_p.h>
+#include <qv4objectproto_p.h>
+#include <qv4numberobject_p.h>
+#include <qv4stringobject_p.h>
+#include <qv4booleanobject_p.h>
+#include <qv4objectiterator_p.h>
+#include <qjsondocument.h>
+#include <qstack.h>
+#include <qstringlist.h>
+
+#include <wtf/MathExtras.h>
+
+using namespace QV4;
+
+//#define PARSER_DEBUG
+#ifdef PARSER_DEBUG
+static int indent = 0;
+#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData()
+#define END --indent
+#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData()
+#else
+#define BEGIN if (1) ; else qDebug()
+#define END do {} while (0)
+#define DEBUG if (1) ; else qDebug()
+#endif
+
+
+class JsonParser
+{
+public:
+ JsonParser(ExecutionContext *context, const QChar *json, int length);
+
+ Value parse(QJsonParseError *error);
+
+private:
+ inline bool eatSpace();
+ inline QChar nextToken();
+
+ Value parseObject();
+ Value parseArray();
+ bool parseMember(Object *o);
+ bool parseString(QString *string);
+ bool parseValue(Value *val);
+ bool parseNumber(Value *val);
+
+ ExecutionContext *context;
+ const QChar *head;
+ const QChar *json;
+ const QChar *end;
+
+ int nestingLevel;
+ QJsonParseError::ParseError lastError;
+};
+
+static const int nestingLimit = 1024;
+
+
+JsonParser::JsonParser(ExecutionContext *context, const QChar *json, int length)
+ : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError)
+{
+ end = json + length;
+}
+
+
+
+/*
+
+begin-array = ws %x5B ws ; [ left square bracket
+
+begin-object = ws %x7B ws ; { left curly bracket
+
+end-array = ws %x5D ws ; ] right square bracket
+
+end-object = ws %x7D ws ; } right curly bracket
+
+name-separator = ws %x3A ws ; : colon
+
+value-separator = ws %x2C ws ; , comma
+
+Insignificant whitespace is allowed before or after any of the six
+structural characters.
+
+ws = *(
+ %x20 / ; Space
+ %x09 / ; Horizontal tab
+ %x0A / ; Line feed or New line
+ %x0D ; Carriage return
+ )
+
+*/
+
+enum {
+ Space = 0x20,
+ Tab = 0x09,
+ LineFeed = 0x0a,
+ Return = 0x0d,
+ BeginArray = 0x5b,
+ BeginObject = 0x7b,
+ EndArray = 0x5d,
+ EndObject = 0x7d,
+ NameSeparator = 0x3a,
+ ValueSeparator = 0x2c,
+ Quote = 0x22
+};
+
+bool JsonParser::eatSpace()
+{
+ while (json < end) {
+ if (*json > Space)
+ break;
+ if (*json != Space &&
+ *json != Tab &&
+ *json != LineFeed &&
+ *json != Return)
+ break;
+ ++json;
+ }
+ return (json < end);
+}
+
+QChar JsonParser::nextToken()
+{
+ if (!eatSpace())
+ return 0;
+ QChar token = *json++;
+ switch (token.unicode()) {
+ case BeginArray:
+ case BeginObject:
+ case NameSeparator:
+ case ValueSeparator:
+ case EndArray:
+ case EndObject:
+ eatSpace();
+ case Quote:
+ break;
+ default:
+ token = 0;
+ break;
+ }
+ return token;
+}
+
+/*
+ JSON-text = object / array
+*/
+Value JsonParser::parse(QJsonParseError *error)
+{
+#ifdef PARSER_DEBUG
+ indent = 0;
+ qDebug() << ">>>>> parser begin";
+#endif
+
+ eatSpace();
+
+ Value v;
+ if (!parseValue(&v)) {
+#ifdef PARSER_DEBUG
+ qDebug() << ">>>>> parser error";
+#endif
+ if (lastError == QJsonParseError::NoError)
+ lastError = QJsonParseError::IllegalValue;
+ error->offset = json - head;
+ error->error = lastError;
+ return Value::undefinedValue();
+ }
+
+ // some input left...
+ if (eatSpace()) {
+ lastError = QJsonParseError::IllegalValue;
+ error->offset = json - head;
+ error->error = lastError;
+ return Value::undefinedValue();
+ }
+
+ END;
+ error->offset = 0;
+ error->error = QJsonParseError::NoError;
+ return v;
+}
+
+/*
+ object = begin-object [ member *( value-separator member ) ]
+ end-object
+*/
+
+Value JsonParser::parseObject()
+{
+ if (++nestingLevel > nestingLimit) {
+ lastError = QJsonParseError::DeepNesting;
+ return Value::undefinedValue();
+ }
+
+ BEGIN << "parseObject pos=" << json;
+
+ Object *o = context->engine->newObject();
+ Value objectVal = Value::fromObject(o);
+
+ QChar token = nextToken();
+ while (token == Quote) {
+ if (!parseMember(o))
+ return Value::undefinedValue();
+ token = nextToken();
+ if (token != ValueSeparator)
+ break;
+ token = nextToken();
+ if (token == EndObject) {
+ lastError = QJsonParseError::MissingObject;
+ return Value::undefinedValue();
+ }
+ }
+
+ DEBUG << "end token=" << token;
+ if (token != EndObject) {
+ lastError = QJsonParseError::UnterminatedObject;
+ return Value::undefinedValue();
+ }
+
+ END;
+
+ --nestingLevel;
+ return objectVal;
+}
+
+/*
+ member = string name-separator value
+*/
+bool JsonParser::parseMember(Object *o)
+{
+ BEGIN << "parseMember";
+
+ QString key;
+ if (!parseString(&key))
+ return false;
+ QChar token = nextToken();
+ if (token != NameSeparator) {
+ lastError = QJsonParseError::MissingNameSeparator;
+ return false;
+ }
+ Value val;
+ if (!parseValue(&val))
+ return false;
+
+ Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data);
+ p->value = val;
+
+ END;
+ return true;
+}
+
+/*
+ array = begin-array [ value *( value-separator value ) ] end-array
+*/
+Value JsonParser::parseArray()
+{
+ BEGIN << "parseArray";
+ ArrayObject *array = context->engine->newArrayObject();
+
+ if (++nestingLevel > nestingLimit) {
+ lastError = QJsonParseError::DeepNesting;
+ return Value::undefinedValue();
+ }
+
+ if (!eatSpace()) {
+ lastError = QJsonParseError::UnterminatedArray;
+ return Value::undefinedValue();
+ }
+ if (*json == EndArray) {
+ nextToken();
+ } else {
+ uint index = 0;
+ while (1) {
+ Value val;
+ if (!parseValue(&val))
+ return Value::undefinedValue();
+ array->arraySet(index, val);
+ QChar token = nextToken();
+ if (token == EndArray)
+ break;
+ else if (token != ValueSeparator) {
+ if (!eatSpace())
+ lastError = QJsonParseError::UnterminatedArray;
+ else
+ lastError = QJsonParseError::MissingValueSeparator;
+ return Value::undefinedValue();
+ }
+ ++index;
+ }
+ }
+
+ DEBUG << "size =" << array->arrayLength();
+ END;
+
+ --nestingLevel;
+ return Value::fromObject(array);
+}
+
+/*
+value = false / null / true / object / array / number / string
+
+*/
+
+bool JsonParser::parseValue(Value *val)
+{
+ BEGIN << "parse Value" << *json;
+
+ switch ((json++)->unicode()) {
+ case 'n':
+ if (end - json < 3) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'u' &&
+ *json++ == 'l' &&
+ *json++ == 'l') {
+ *val = Value::nullValue();
+ DEBUG << "value: null";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case 't':
+ if (end - json < 3) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'r' &&
+ *json++ == 'u' &&
+ *json++ == 'e') {
+ *val = Value::fromBoolean(true);
+ DEBUG << "value: true";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case 'f':
+ if (end - json < 4) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'a' &&
+ *json++ == 'l' &&
+ *json++ == 's' &&
+ *json++ == 'e') {
+ *val = Value::fromBoolean(false);
+ DEBUG << "value: false";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case Quote: {
+ QString value;
+ if (!parseString(&value))
+ return false;
+ DEBUG << "value: string";
+ END;
+ *val = Value::fromString(context, value);
+ return true;
+ }
+ case BeginArray: {
+ *val = parseArray();
+ if (val->isUndefined())
+ return false;
+ DEBUG << "value: array";
+ END;
+ return true;
+ }
+ case BeginObject: {
+ *val = parseObject();
+ if (val->isUndefined())
+ return false;
+ DEBUG << "value: object";
+ END;
+ return true;
+ }
+ case EndArray:
+ lastError = QJsonParseError::MissingObject;
+ return false;
+ default:
+ --json;
+ if (!parseNumber(val))
+ return false;
+ DEBUG << "value: number";
+ END;
+ }
+
+ return true;
+}
+
+
+
+
+
+/*
+ number = [ minus ] int [ frac ] [ exp ]
+ decimal-point = %x2E ; .
+ digit1-9 = %x31-39 ; 1-9
+ e = %x65 / %x45 ; e E
+ exp = e [ minus / plus ] 1*DIGIT
+ frac = decimal-point 1*DIGIT
+ int = zero / ( digit1-9 *DIGIT )
+ minus = %x2D ; -
+ plus = %x2B ; +
+ zero = %x30 ; 0
+
+*/
+
+bool JsonParser::parseNumber(Value *val)
+{
+ BEGIN << "parseNumber" << *json;
+
+ const QChar *start = json;
+ bool isInt = true;
+
+ // minus
+ if (json < end && *json == '-')
+ ++json;
+
+ // int = zero / ( digit1-9 *DIGIT )
+ if (json < end && *json == '0') {
+ ++json;
+ } else {
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // frac = decimal-point 1*DIGIT
+ if (json < end && *json == '.') {
+ isInt = false;
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // exp = e [ minus / plus ] 1*DIGIT
+ if (json < end && (*json == 'e' || *json == 'E')) {
+ isInt = false;
+ ++json;
+ if (json < end && (*json == '-' || *json == '+'))
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ QString number(start, json - start);
+ DEBUG << "numberstring" << number;
+
+ if (isInt) {
+ bool ok;
+ int n = number.toInt(&ok);
+ if (ok && n < (1<<25) && n > -(1<<25)) {
+ *val = Value::fromInt32(n);
+ END;
+ return true;
+ }
+ }
+
+ bool ok;
+ double d;
+ d = number.toDouble(&ok);
+
+ if (!ok) {
+ lastError = QJsonParseError::IllegalNumber;
+ return false;
+ }
+
+ * val = Value::fromDouble(d);
+
+ END;
+ return true;
+}
+
+/*
+
+ string = quotation-mark *char quotation-mark
+
+ char = unescaped /
+ escape (
+ %x22 / ; " quotation mark U+0022
+ %x5C / ; \ reverse solidus U+005C
+ %x2F / ; / solidus U+002F
+ %x62 / ; b backspace U+0008
+ %x66 / ; f form feed U+000C
+ %x6E / ; n line feed U+000A
+ %x72 / ; r carriage return U+000D
+ %x74 / ; t tab U+0009
+ %x75 4HEXDIG ) ; uXXXX U+XXXX
+
+ escape = %x5C ; \
+
+ quotation-mark = %x22 ; "
+
+ unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ */
+static inline bool addHexDigit(QChar digit, uint *result)
+{
+ ushort d = digit.unicode();
+ *result <<= 4;
+ if (d >= '0' && d <= '9')
+ *result |= (d - '0');
+ else if (d >= 'a' && d <= 'f')
+ *result |= (d - 'a') + 10;
+ else if (d >= 'A' && d <= 'F')
+ *result |= (d - 'A') + 10;
+ else
+ return false;
+ return true;
+}
+
+static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch)
+{
+ ++json;
+ if (json >= end)
+ return false;
+
+ DEBUG << "scan escape";
+ uint escaped = (json++)->unicode();
+ switch (escaped) {
+ case '"':
+ *ch = '"'; break;
+ case '\\':
+ *ch = '\\'; break;
+ case '/':
+ *ch = '/'; break;
+ case 'b':
+ *ch = 0x8; break;
+ case 'f':
+ *ch = 0xc; break;
+ case 'n':
+ *ch = 0xa; break;
+ case 'r':
+ *ch = 0xd; break;
+ case 't':
+ *ch = 0x9; break;
+ case 'u': {
+ *ch = 0;
+ if (json > end - 4)
+ return false;
+ for (int i = 0; i < 4; ++i) {
+ if (!addHexDigit(*json, ch))
+ return false;
+ ++json;
+ }
+ if (*ch <= 0x1f)
+ return false;
+ return true;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool JsonParser::parseString(QString *string)
+{
+ BEGIN << "parse string stringPos=" << json;
+
+ while (json < end) {
+ if (*json == '"')
+ break;
+ else if (*json == '\\') {
+ uint ch = 0;
+ if (!scanEscapeSequence(json, end, &ch)) {
+ lastError = QJsonParseError::IllegalEscapeSequence;
+ return false;
+ }
+ qDebug() << "scanEscape" << hex << ch;
+ if (QChar::requiresSurrogates(ch)) {
+ *string += QChar::highSurrogate(ch);
+ *string += QChar::lowSurrogate(ch);
+ } else {
+ *string += QChar(ch);
+ }
+ } else {
+ if (json->unicode() <= 0x1f) {
+ lastError = QJsonParseError::IllegalEscapeSequence;
+ return false;
+ }
+ *string += *json;
+ ++json;
+ }
+ }
+ ++json;
+
+ if (json > end) {
+ lastError = QJsonParseError::UnterminatedString;
+ return false;
+ }
+
+ END;
+ return true;
+}
+
+
+struct Stringify
+{
+ ExecutionContext *ctx;
+ FunctionObject *replacerFunction;
+ QVector<String *> propertyList;
+ QString gap;
+ QString indent;
+
+ QStack<Object *> stack;
+
+ Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {}
+
+ QString Str(const QString &key, Value value);
+ QString JA(ArrayObject *a);
+ QString JO(Object *o);
+
+ QString makeMember(const QString &key, Value v);
+};
+
+static QString quote(const QString &str)
+{
+ QString product = "\"";
+ for (int i = 0; i < str.length(); ++i) {
+ QChar c = str.at(i);
+ switch (c.unicode()) {
+ case '"':
+ product += "\\\"";
+ break;
+ case '\\':
+ product += "\\\\";
+ break;
+ case '\b':
+ product += "\\b";
+ break;
+ case '\f':
+ product += "\\f";
+ break;
+ case '\n':
+ product += "\\n";
+ break;
+ case '\r':
+ product += "\\r";
+ break;
+ case '\t':
+ product += "\\t";
+ break;
+ default:
+ if (c.unicode() <= 0x1f) {
+ product += "\\u00";
+ product += c.unicode() > 0xf ? '1' : '0';
+ product += "0123456789abcdef"[c.unicode() & 0xf];
+ } else {
+ product += c;
+ }
+ }
+ }
+ product += '"';
+ return product;
+}
+
+QString Stringify::Str(const QString &key, Value value)
+{
+ QString result;
+
+ if (Object *o = value.asObject()) {
+ FunctionObject *toJSON = o->get(ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject();
+ if (toJSON) {
+ Value arg = Value::fromString(ctx, key);
+ value = toJSON->call(value, &arg, 1);
+ }
+ }
+
+ if (replacerFunction) {
+ Object *holder = ctx->engine->newObject();
+ Value holderValue = Value::fromObject(holder);
+ holder->put(ctx, QString(), value);
+ Value args[2];
+ args[0] = Value::fromString(ctx, key);
+ args[1] = value;
+ value = replacerFunction->call(holderValue, args, 2);
+ }
+
+ if (Object *o = value.asObject()) {
+ if (NumberObject *n = o->asNumberObject())
+ value = n->value;
+ else if (StringObject *so = o->asStringObject())
+ value = so->value;
+ else if (BooleanObject *b =o->asBooleanObject())
+ value = b->value;
+ }
+
+ if (value.isNull())
+ return 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.toString(ctx)->toQString() : QStringLiteral("null");
+ }
+
+ if (Object *o = value.asObject()) {
+ if (!o->asFunctionObject()) {
+ if (o->asArrayObject())
+ return JA(static_cast<ArrayObject *>(o));
+ else
+ return JO(o);
+ }
+ }
+
+ return QString();
+}
+
+QString Stringify::makeMember(const QString &key, Value v)
+{
+ QString strP = Str(key, v);
+ if (!strP.isEmpty()) {
+ QString member = quote(key) + ':';
+ if (!gap.isEmpty())
+ member += ' ';
+ member += strP;
+ return member;
+ }
+ return QString();
+}
+
+QString Stringify::JO(Object *o)
+{
+ if (stack.contains(o))
+ ctx->throwTypeError();
+
+ QString result;
+ stack.push(o);
+ QString stepback = indent;
+ indent += gap;
+
+ QStringList partial;
+ if (propertyList.isEmpty()) {
+ ObjectIterator it(o, ObjectIterator::EnumerableOnly);
+
+ while (1) {
+ Value v;
+ Value name = it.nextPropertyNameAsString(&v);
+ if (name.isNull())
+ break;
+ QString key = name.toQString();
+ QString member = makeMember(key, v);
+ if (!member.isEmpty())
+ partial += member;
+ }
+ } else {
+ for (int i = 0; i < propertyList.size(); ++i) {
+ bool exists;
+ Value v = o->get(propertyList.at(i), &exists);
+ if (!exists)
+ continue;
+ QString member = makeMember(propertyList.at(i)->toQString(), v);
+ if (!member.isEmpty())
+ partial += member;
+ }
+ }
+
+ if (partial.isEmpty()) {
+ result = QStringLiteral("{}");
+ } else if (gap.isEmpty()) {
+ result = "{" + partial.join(",") + "}";
+ } else {
+ QString separator = ",\n" + indent;
+ result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}";
+ }
+
+ indent = stepback;
+ stack.pop();
+ return result;
+}
+
+QString Stringify::JA(ArrayObject *a)
+{
+ if (stack.contains(a))
+ ctx->throwTypeError();
+
+ QString result;
+ stack.push(a);
+ QString stepback = indent;
+ indent += gap;
+
+ QStringList partial;
+ uint len = a->arrayLength();
+ for (uint i = 0; i < len; ++i) {
+ bool exists;
+ Value v = a->getIndexed(i, &exists);
+ if (!exists) {
+ partial += QStringLiteral("null");
+ continue;
+ }
+ QString strP = Str(QString::number(i), v);
+ if (!strP.isEmpty())
+ partial += strP;
+ else
+ partial += QStringLiteral("null");
+ }
+
+ if (partial.isEmpty()) {
+ result = QStringLiteral("[]");
+ } else if (gap.isEmpty()) {
+ result = "[" + partial.join(",") + "]";
+ } else {
+ QString separator = ",\n" + indent;
+ result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]";
+ }
+
+ indent = stepback;
+ stack.pop();
+ return result;
+}
+
+
+JsonObject::JsonObject(ExecutionContext *context)
+ : Object(context->engine)
+{
+ type = Type_JSONObject;
+ prototype = context->engine->objectPrototype;
+
+ defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2);
+ defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3);
+}
+
+
+Value JsonObject::method_parse(SimpleCallContext *ctx)
+{
+ QString jtext = ctx->argument(0).toString(ctx)->toQString();
+
+ DEBUG << "parsing source = " << jtext;
+ JsonParser parser(ctx, jtext.constData(), jtext.length());
+ QJsonParseError error;
+ Value result = parser.parse(&error);
+ if (error.error != QJsonParseError::NoError) {
+ DEBUG << "parse error" << error.errorString();
+ ctx->throwSyntaxError(0);
+ }
+
+ return result;
+}
+
+Value JsonObject::method_stringify(SimpleCallContext *ctx)
+{
+ Stringify stringify(ctx);
+
+ Object *o = ctx->argument(1).asObject();
+ if (o) {
+ stringify.replacerFunction = o->asFunctionObject();
+ if (o->isArrayObject()) {
+ uint arrayLen = o->arrayLength();
+ for (uint i = 0; i < arrayLen; ++i) {
+ Value v = o->getIndexed(i);
+ if (v.asNumberObject() || v.asStringObject() || v.isNumber())
+ v = __qmljs_to_string(v, ctx);
+ if (v.isString()) {
+ String *s = v.stringValue();
+ if (!stringify.propertyList.contains(s))
+ stringify.propertyList.append(s);
+ }
+ }
+ }
+ }
+
+ Value s = ctx->argument(2);
+ if (NumberObject *n = s.asNumberObject())
+ s = n->value;
+ else if (StringObject *so = s.asStringObject())
+ s = so->value;
+
+ if (s.isNumber()) {
+ stringify.gap = QString(qMin(10, (int)s.toInteger()), ' ');
+ } else if (s.isString()) {
+ stringify.gap = s.stringValue()->toQString().left(10);
+ }
+
+
+ QString result = stringify.Str(QString(), ctx->argument(0));
+ if (result.isEmpty())
+ return Value::undefinedValue();
+ return Value::fromString(ctx, result);
+}
+
+
+
+QV4::Value JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value)
+{
+ if (value.isString())
+ return Value::fromString(engine->current, value.toString());
+ else if (value.isDouble())
+ return Value::fromDouble(value.toDouble());
+ else if (value.isBool())
+ return Value::fromBoolean(value.toBool());
+ else if (value.isArray())
+ return fromJsonArray(engine, value.toArray());
+ else if (value.isObject())
+ return fromJsonObject(engine, value.toObject());
+ else if (value.isNull())
+ return Value::nullValue();
+ else
+ return Value::undefinedValue();
+}
+
+QJsonValue JsonObject::toJsonValue(const QV4::Value &value,
+ V4ObjectSet &visitedObjects)
+{
+ if (String *s = value.asString())
+ return QJsonValue(s->toQString());
+ else if (value.isNumber())
+ return QJsonValue(value.toNumber());
+ else if (value.isBoolean())
+ return QJsonValue((bool)value.booleanValue());
+ else if (ArrayObject *a = value.asArrayObject())
+ return toJsonArray(a, visitedObjects);
+ else if (Object *o = value.asObject())
+ return toJsonObject(o, visitedObjects);
+ else if (value.isNull())
+ return QJsonValue(QJsonValue::Null);
+ else
+ return QJsonValue(QJsonValue::Undefined);
+}
+
+QV4::Value JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object)
+{
+ Object *o = engine->newObject();
+ for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it)
+ o->put(engine->newString(it.key()), fromJsonValue(engine, it.value()));
+ return Value::fromObject(o);
+}
+
+QJsonObject JsonObject::toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects)
+{
+ QJsonObject result;
+ if (!o || o->asFunctionObject())
+ return result;
+
+ if (visitedObjects.contains(o)) {
+ // Avoid recursion.
+ // For compatibility with QVariant{List,Map} conversion, we return an
+ // empty object (and no error is thrown).
+ return result;
+ }
+
+ visitedObjects.insert(o);
+
+ ObjectIterator it(o, ObjectIterator::EnumerableOnly);
+ while (1) {
+ Value v;
+ Value name = it.nextPropertyNameAsString(&v);
+ if (name.isNull())
+ break;
+
+ QString key = name.toQString();
+ if (!v.asFunctionObject())
+ result.insert(key, toJsonValue(v, visitedObjects));
+ }
+
+ visitedObjects.remove(o);
+
+ return result;
+}
+
+QV4::Value JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array)
+{
+ int size = array.size();
+ ArrayObject *a = engine->newArrayObject();
+ a->arrayReserve(size);
+ a->arrayDataLen = size;
+ for (int i = 0; i < size; i++)
+ a->arrayData[i].value = fromJsonValue(engine, array.at(i));
+ a->setArrayLengthUnchecked(size);
+ return Value::fromObject(a);
+}
+
+QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects)
+{
+ QJsonArray result;
+ if (!a)
+ return result;
+
+ if (visitedObjects.contains(a)) {
+ // Avoid recursion.
+ // For compatibility with QVariant{List,Map} conversion, we return an
+ // empty array (and no error is thrown).
+ return result;
+ }
+
+ visitedObjects.insert(a);
+
+ quint32 length = a->arrayLength();
+ for (quint32 i = 0; i < length; ++i) {
+ Value v = a->getIndexed(i);
+ result.append(toJsonValue(v.asFunctionObject() ? QV4::Value::nullValue() : v, visitedObjects));
+ }
+
+ visitedObjects.remove(a);
+
+ return result;
+}
diff --git a/src/qml/qml/v4/qv4jsonobject_p.h b/src/qml/qml/v4/qv4jsonobject_p.h
new file mode 100644
index 0000000000..ccd99d5488
--- /dev/null
+++ b/src/qml/qml/v4/qv4jsonobject_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4JSONOBJECTS_H
+#define QV4SJONOBJECTS_H
+
+#include "qv4object_p.h"
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qjsonvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct JsonObject : Object {
+private:
+ typedef QSet<QV4::Object *> V4ObjectSet;
+public:
+ JsonObject(ExecutionContext *context);
+
+ static Value method_parse(SimpleCallContext *ctx);
+ static Value method_stringify(SimpleCallContext *ctx);
+
+ static QV4::Value fromJsonValue(ExecutionEngine *engine, const QJsonValue &value);
+ static QV4::Value fromJsonObject(ExecutionEngine *engine, const QJsonObject &object);
+ static QV4::Value fromJsonArray(ExecutionEngine *engine, const QJsonArray &array);
+
+ static inline QJsonValue toJsonValue(const QV4::Value &value)
+ { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); }
+ static inline QJsonObject toJsonObject(QV4::Object *o)
+ { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); }
+ static inline QJsonArray toJsonArray(QV4::ArrayObject *a)
+ { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); }
+
+private:
+ static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects);
+ static QJsonObject toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects);
+ static QJsonArray toJsonArray(QV4::ArrayObject *a, V4ObjectSet &visitedObjects);
+
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/qml/qml/v4/qv4lookup.cpp b/src/qml/qml/v4/qv4lookup.cpp
new file mode 100644
index 0000000000..b5ea877bd4
--- /dev/null
+++ b/src/qml/qml/v4/qv4lookup.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4lookup_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+Property *Lookup::lookup(Object *obj, PropertyAttributes *attrs)
+{
+ int i = 0;
+ while (i < level && obj && obj->internalClass == classList[i]) {
+ obj = obj->prototype;
+ ++i;
+ }
+
+ if (index != UINT_MAX && obj->internalClass == classList[i]) {
+ *attrs = obj->internalClass->propertyData.at(index);
+ return obj->memberData + index;
+ }
+
+ while (i < Size && obj) {
+ classList[i] = obj->internalClass;
+
+ index = obj->internalClass->find(name);
+ if (index != UINT_MAX) {
+ level = i;
+ *attrs = obj->internalClass->propertyData.at(index);
+ return obj->memberData + index;
+ }
+
+ obj = obj->prototype;
+ ++i;
+ }
+ level = i;
+
+ while (obj) {
+ index = obj->internalClass->find(name);
+ if (index != UINT_MAX) {
+ *attrs = obj->internalClass->propertyData.at(index);
+ return obj->memberData + index;
+ }
+
+ obj = obj->prototype;
+ }
+ return 0;
+}
+
+
+void Lookup::getterGeneric(QV4::Lookup *l, QV4::Value *result, const QV4::Value &object)
+{
+ if (Object *o = object.asObject()) {
+ o->getLookup(l, result);
+ return;
+ }
+
+ Value res;
+ if (Managed *m = object.asManaged()) {
+ res = m->get(l->name);
+ } else {
+ ExecutionContext *ctx = l->name->engine()->current;
+ Object *o = __qmljs_convert_to_object(ctx, object);
+ res = o->get(l->name);
+ }
+ if (result)
+ *result = res;
+}
+
+void Lookup::getter0(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass) {
+ if (result)
+ *result = o->memberData[l->index].value;
+ return;
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+void Lookup::getter1(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass &&
+ l->classList[1] == o->prototype->internalClass) {
+ if (result)
+ *result = o->prototype->memberData[l->index].value;
+ return;
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+void Lookup::getter2(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[1] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[2] == o->internalClass) {
+ if (result)
+ *result = o->memberData[l->index].value;
+ return;
+ }
+ }
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+void Lookup::getterAccessor0(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass) {
+ Value res;
+ FunctionObject *getter = o->memberData[l->index].getter();
+ if (!getter)
+ res = Value::undefinedValue();
+ else
+ res = getter->call(object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+void Lookup::getterAccessor1(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass &&
+ l->classList[1] == o->prototype->internalClass) {
+ Value res;
+ FunctionObject *getter = o->prototype->memberData[l->index].getter();
+ if (!getter)
+ res = Value::undefinedValue();
+ else
+ res = getter->call(object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+void Lookup::getterAccessor2(Lookup *l, Value *result, const Value &object)
+{
+ if (Object *o = object.asObject()) {
+ if (l->classList[0] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[1] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[2] == o->internalClass) {
+ Value res;
+ FunctionObject *getter = o->memberData[l->index].getter();
+ if (!getter)
+ res = Value::undefinedValue();
+ else
+ res = getter->call(object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ }
+ }
+ l->getter = getterGeneric;
+ getterGeneric(l, result, object);
+}
+
+
+void Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ PropertyAttributes attrs;
+ Property *p = l->lookup(o, &attrs);
+ if (p) {
+ if (attrs.isData()) {
+ if (l->level == 0)
+ l->globalGetter = globalGetter0;
+ else if (l->level == 1)
+ l->globalGetter = globalGetter1;
+ else if (l->level == 2)
+ l->globalGetter = globalGetter2;
+ *result = p->value;
+ return;
+ } else {
+ if (l->level == 0)
+ l->globalGetter = globalGetterAccessor0;
+ else if (l->level == 1)
+ l->globalGetter = globalGetterAccessor1;
+ else if (l->level == 2)
+ l->globalGetter = globalGetterAccessor2;
+ Value res = o->getValue(p, attrs);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ ctx->throwReferenceError(Value::fromString(l->name));
+}
+
+void Lookup::globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass) {
+ *result = o->memberData[l->index].value;
+ return;
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass &&
+ l->classList[1] == o->prototype->internalClass) {
+ *result = o->prototype->memberData[l->index].value;
+ return;
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[1] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[2] == o->internalClass) {
+ *result = o->prototype->memberData[l->index].value;
+ return;
+ }
+ }
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass) {
+ FunctionObject *getter = o->memberData[l->index].getter();
+ if (!getter)
+ *result = Value::undefinedValue();
+ else
+ *result = getter->call(Value::undefinedValue(), 0, 0);
+ return;
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass &&
+ l->classList[1] == o->prototype->internalClass) {
+ FunctionObject *getter = o->prototype->memberData[l->index].getter();
+ if (!getter)
+ *result = Value::undefinedValue();
+ else
+ *result = getter->call(Value::undefinedValue(), 0, 0);
+ return;
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result)
+{
+ Object *o = ctx->engine->globalObject;
+ if (l->classList[0] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[1] == o->internalClass) {
+ o = o->prototype;
+ if (l->classList[2] == o->internalClass) {
+ FunctionObject *getter = o->memberData[l->index].getter();
+ if (!getter)
+ *result = Value::undefinedValue();
+ else
+ *result = getter->call(Value::undefinedValue(), 0, 0);
+ return;
+ }
+ }
+ }
+ l->globalGetter = globalGetterGeneric;
+ globalGetterGeneric(l, ctx, result);
+}
+
+void Lookup::setterGeneric(Lookup *l, const Value &object, const Value &value)
+{
+ Object *o = object.asObject();
+ if (!o) {
+ o = __qmljs_convert_to_object(l->name->engine()->current, object);
+ o->put(l->name, value);
+ return;
+ }
+ o->setLookup(l, value);
+}
+
+void Lookup::setter0(Lookup *l, const Value &object, const Value &value)
+{
+ Object *o = object.asObject();
+ if (o && o->internalClass == l->classList[0]) {
+ o->memberData[l->index].value = value;
+ return;
+ }
+
+ l->setter = setterGeneric;
+ setterGeneric(l, object, value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4lookup_p.h b/src/qml/qml/v4/qv4lookup_p.h
new file mode 100644
index 0000000000..e77552826a
--- /dev/null
+++ b/src/qml/qml/v4/qv4lookup_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4LOOKUP_H
+#define QV4LOOKUP_H
+
+#include "qv4global_p.h"
+#include "qv4runtime_p.h"
+#include "qv4engine_p.h"
+#include "qv4context_p.h"
+#include "qv4object_p.h"
+#include "qv4internalclass_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Lookup {
+ enum { Size = 3 };
+ union {
+ void (*getter)(Lookup *l, Value *result, const Value &object);
+ void (*globalGetter)(Lookup *l, ExecutionContext *ctx, Value *result);
+ void (*setter)(Lookup *l, const Value &object, const Value &v);
+ };
+ InternalClass *classList[Size];
+ int level;
+ uint index;
+ String *name;
+
+ static void getterGeneric(Lookup *l, Value *result, const Value &object);
+ static void getter0(Lookup *l, Value *result, const Value &object);
+ static void getter1(Lookup *l, Value *result, const Value &object);
+ static void getter2(Lookup *l, Value *result, const Value &object);
+ static void getterAccessor0(Lookup *l, Value *result, const Value &object);
+ static void getterAccessor1(Lookup *l, Value *result, const Value &object);
+ static void getterAccessor2(Lookup *l, Value *result, const Value &object);
+
+ static void globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result);
+
+ static void setterGeneric(Lookup *l, const Value &object, const Value &value);
+ static void setter0(Lookup *l, const Value &object, const Value &value);
+
+ Property *lookup(Object *obj, PropertyAttributes *attrs);
+
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4managed.cpp b/src/qml/qml/v4/qv4managed.cpp
new file mode 100644
index 0000000000..19adb354e3
--- /dev/null
+++ b/src/qml/qml/v4/qv4managed.cpp
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4managed_p.h"
+#include "qv4mm_p.h"
+#include "qv4errorobject_p.h"
+
+using namespace QV4;
+
+const ManagedVTable Managed::static_vtbl =
+{
+ call,
+ construct,
+ 0 /*markObjects*/,
+ destroy,
+ 0 /*collectDeletables*/,
+ hasInstance,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ isEqualTo,
+ 0,
+ "Managed",
+};
+
+
+void *Managed::operator new(size_t size, MemoryManager *mm)
+{
+ assert(mm);
+
+ return mm->allocManaged(size);
+}
+
+void Managed::operator delete(void *ptr)
+{
+ if (!ptr)
+ return;
+
+ Managed *m = static_cast<Managed *>(ptr);
+ m->vtbl = 0;
+ m->_data = 0;
+ m->~Managed();
+}
+
+void Managed::operator delete(void *ptr, MemoryManager *mm)
+{
+ Q_UNUSED(mm);
+
+ operator delete(ptr);
+}
+
+ExecutionEngine *Managed::engine() const
+{
+ return internalClass ? internalClass->engine : 0;
+}
+
+QString Managed::className() const
+{
+ const char *s = 0;
+ switch (Type(type)) {
+ case Type_Invalid:
+ case Type_String:
+ return QString();
+ case Type_Object:
+ s = "Object";
+ break;
+ case Type_ArrayObject:
+ s = "Array";
+ break;
+ case Type_FunctionObject:
+ s = "Function";
+ break;
+ case Type_BooleanObject:
+ s = "Boolean";
+ break;
+ case Type_NumberObject:
+ s = "Number";
+ break;
+ case Type_StringObject:
+ s = "String";
+ break;
+ case Type_DateObject:
+ s = "Date";
+ break;
+ case Type_RegExpObject:
+ s = "RegExp";
+ break;
+ case Type_ErrorObject:
+ switch (ErrorObject::ErrorType(subtype)) {
+ case ErrorObject::Error:
+ s = "Error";
+ break;
+ case ErrorObject::EvalError:
+ s = "EvalError";
+ break;
+ case ErrorObject::RangeError:
+ s = "RangeError";
+ break;
+ case ErrorObject::ReferenceError:
+ s = "ReferenceError";
+ break;
+ case ErrorObject::SyntaxError:
+ s = "SyntaxError";
+ break;
+ case ErrorObject::TypeError:
+ s = "TypeError";
+ break;
+ case ErrorObject::URIError:
+ s = "URIError";
+ break;
+ }
+ break;
+ case Type_ArgumentsObject:
+ s = "Arguments";
+ break;
+ case Type_JSONObject:
+ s = "JSON";
+ break;
+ case Type_MathObject:
+ s = "Math";
+ break;
+ case Type_ForeachIteratorObject:
+ s = "__ForeachIterator";
+ break;
+ }
+ return QString::fromLatin1(s);
+}
+
+bool Managed::hasInstance(Managed *m, const Value &)
+{
+ m->engine()->current->throwTypeError();
+}
+
+Value Managed::construct(Managed *m, Value *, int)
+{
+ m->engine()->current->throwTypeError();
+}
+
+Value Managed::call(Managed *m, const Value &, Value *, int)
+{
+ m->engine()->current->throwTypeError();
+}
+
+void Managed::getLookup(Managed *m, Lookup *, Value *)
+{
+ m->engine()->current->throwTypeError();
+}
+
+void Managed::setLookup(Managed *m, Lookup *, const Value &)
+{
+ m->engine()->current->throwTypeError();
+}
+
+bool Managed::isEqualTo(Managed *, Managed *)
+{
+ return false;
+}
+
+Value Managed::get(String *name, bool *hasProperty)
+{
+ return vtbl->get(this, name, hasProperty);
+}
+
+Value Managed::getIndexed(uint index, bool *hasProperty)
+{
+ return vtbl->getIndexed(this, index, hasProperty);
+}
diff --git a/src/qml/qml/v4/qv4managed_p.h b/src/qml/qml/v4/qv4managed_p.h
new file mode 100644
index 0000000000..5e3c142bb8
--- /dev/null
+++ b/src/qml/qml/v4/qv4managed_p.h
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_MANAGED_H
+#define QMLJS_MANAGED_H
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QDebug>
+#include "qv4global_p.h"
+#include "qv4value_def_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+#define Q_MANAGED_CHECK \
+ template <typename T> inline void qt_check_for_QMANAGED_macro(const T &_q_argument) const \
+ { int i = qYouForgotTheQ_MANAGED_Macro(this, &_q_argument); i = i + 1; }
+
+template <typename T>
+inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; }
+
+template <typename T1, typename T2>
+inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
+
+#define Q_MANAGED \
+ public: \
+ Q_MANAGED_CHECK \
+ static const QV4::ManagedVTable static_vtbl;
+
+struct GCDeletable
+{
+ GCDeletable() : next(0), lastCall(false) {}
+ virtual ~GCDeletable() {}
+ GCDeletable *next;
+ bool lastCall;
+};
+
+struct ManagedVTable
+{
+ Value (*call)(Managed *, const Value &thisObject, Value *args, int argc);
+ Value (*construct)(Managed *, Value *args, int argc);
+ void (*markObjects)(Managed *);
+ void (*destroy)(Managed *);
+ void (*collectDeletables)(Managed *, GCDeletable **deletable);
+ bool (*hasInstance)(Managed *, const Value &value);
+ Value (*get)(Managed *, String *name, bool *hasProperty);
+ Value (*getIndexed)(Managed *, uint index, bool *hasProperty);
+ void (*put)(Managed *, String *name, const Value &value);
+ void (*putIndexed)(Managed *, uint index, const Value &value);
+ PropertyAttributes (*query)(const Managed *, String *name);
+ PropertyAttributes (*queryIndexed)(const Managed *, uint index);
+ bool (*deleteProperty)(Managed *m, String *name);
+ bool (*deleteIndexedProperty)(Managed *m, uint index);
+ void (*getLookup)(Managed *m, Lookup *l, Value *result);
+ void (*setLookup)(Managed *m, Lookup *l, const Value &v);
+ bool (*isEqualTo)(Managed *m, Managed *other);
+ Property *(*advanceIterator)(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes);
+ const char *className;
+};
+
+#define DEFINE_MANAGED_VTABLE(classname) \
+const QV4::ManagedVTable classname::static_vtbl = \
+{ \
+ call, \
+ construct, \
+ markObjects, \
+ destroy, \
+ 0, \
+ hasInstance, \
+ get, \
+ getIndexed, \
+ put, \
+ putIndexed, \
+ query, \
+ queryIndexed, \
+ deleteProperty, \
+ deleteIndexedProperty, \
+ getLookup, \
+ setLookup, \
+ isEqualTo, \
+ advanceIterator, \
+ #classname \
+}
+
+#define DEFINE_MANAGED_VTABLE_WITH_DELETABLES(classname) \
+const QV4::ManagedVTable classname::static_vtbl = \
+{ \
+ call, \
+ construct, \
+ markObjects, \
+ destroy, \
+ collectDeletables, \
+ hasInstance, \
+ get, \
+ getIndexed, \
+ put, \
+ putIndexed, \
+ query, \
+ queryIndexed, \
+ deleteProperty, \
+ deleteIndexedProperty, \
+ getLookup, \
+ setLookup, \
+ isEqualTo, \
+ advanceIterator, \
+ #classname \
+}
+
+struct Q_QML_EXPORT Managed
+{
+private:
+ void *operator new(size_t);
+ Managed(const Managed &other);
+ void operator = (const Managed &other);
+
+protected:
+ Managed(InternalClass *internal)
+ : _data(0), vtbl(&static_vtbl), internalClass(internal)
+ { inUse = 1; extensible = 1; }
+
+public:
+ void *operator new(size_t size, MemoryManager *mm);
+ void operator delete(void *ptr);
+ void operator delete(void *ptr, MemoryManager *mm);
+
+ inline void mark() {
+ if (markBit)
+ return;
+ markBit = 1;
+ if (vtbl->markObjects)
+ vtbl->markObjects(this);
+ }
+
+ enum Type {
+ Type_Invalid,
+ Type_String,
+ Type_Object,
+ Type_ArrayObject,
+ Type_FunctionObject,
+ Type_BooleanObject,
+ Type_NumberObject,
+ Type_StringObject,
+ Type_DateObject,
+ Type_RegExpObject,
+ Type_ErrorObject,
+ Type_ArgumentsObject,
+ Type_JSONObject,
+ Type_MathObject,
+ Type_ForeachIteratorObject,
+ Type_RegExp,
+
+ Type_QmlSequence
+ };
+
+ ExecutionEngine *engine() const;
+
+ template <typename T>
+ T *as() {
+#if !defined(QT_NO_QOBJECT_CHECK)
+ reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(this));
+#endif
+ return vtbl == &T::static_vtbl ? static_cast<T *>(this) : 0;
+ }
+ template <typename T>
+ const T *as() const {
+#if !defined(QT_NO_QOBJECT_CHECK)
+ reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(const_cast<Managed *>(this)));
+#endif
+ return vtbl == &T::static_vtbl ? static_cast<const T *>(this) : 0;
+ }
+
+ ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; }
+ FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; }
+ BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; }
+ NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; }
+ StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; }
+ DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; }
+ ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; }
+ ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; }
+
+ bool isListType() const { return type == Type_QmlSequence; }
+
+ bool isArrayObject() const { return type == Type_ArrayObject; }
+ bool isStringObject() const { return type == Type_StringObject; }
+
+ QString className() const;
+
+ Managed **nextFreeRef() {
+ return reinterpret_cast<Managed **>(this);
+ }
+ Managed *nextFree() {
+ return *reinterpret_cast<Managed **>(this);
+ }
+ void setNextFree(Managed *m) {
+ *reinterpret_cast<Managed **>(this) = m;
+ }
+
+ inline bool hasInstance(const Value &v) {
+ return vtbl->hasInstance(this, v);
+ }
+ Value construct(Value *args, int argc);
+ Value call(const Value &thisObject, Value *args, int argc);
+ Value get(String *name, bool *hasProperty = 0);
+ Value getIndexed(uint index, bool *hasProperty = 0);
+ void put(String *name, const Value &value)
+ { vtbl->put(this, name, value); }
+ void putIndexed(uint index, const Value &value)
+ { vtbl->putIndexed(this, index, value); }
+ PropertyAttributes query(String *name) const
+ { return vtbl->query(this, name); }
+ PropertyAttributes queryIndexed(uint index) const
+ { return vtbl->queryIndexed(this, index); }
+
+ bool deleteProperty(String *name)
+ { return vtbl->deleteProperty(this, name); }
+ bool deleteIndexedProperty(uint index)
+ { return vtbl->deleteIndexedProperty(this, index); }
+ void getLookup(Lookup *l, Value *result)
+ { vtbl->getLookup(this, l, result); }
+ void setLookup(Lookup *l, const Value &v)
+ { vtbl->setLookup(this, l, v); }
+
+ bool isEqualTo(Managed *other)
+ { return vtbl->isEqualTo(this, other); }
+ Property *advanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes)
+ { return vtbl->advanceIterator(this, it, name, index, attributes); }
+
+ static void destroy(Managed *that) { that->_data = 0; }
+ static bool hasInstance(Managed *that, const Value &value);
+ static Value construct(Managed *m, Value *, int);
+ static Value call(Managed *m, const Value &, Value *, int);
+ static void getLookup(Managed *m, Lookup *, Value *);
+ static void setLookup(Managed *m, Lookup *l, const Value &v);
+ static bool isEqualTo(Managed *m, Managed *other);
+
+ uint internalType() const {
+ return type;
+ }
+
+ union {
+ uint _data;
+ struct {
+ uint markBit : 1;
+ uint inUse : 1;
+ uint extensible : 1; // used by Object
+ uint isNonStrictArgumentsObject : 1;
+ uint isBuiltinFunction : 1; // used by FunctionObject
+ uint needsActivation : 1; // used by FunctionObject
+ uint usesArgumentsObject : 1; // used by FunctionObject
+ uint strictMode : 1; // used by FunctionObject
+ uint type : 8;
+ mutable uint subtype : 3;
+ uint bindingKeyFlag : 1;
+ uint unused : 12;
+ };
+ };
+
+protected:
+
+ static const ManagedVTable static_vtbl;
+
+ const ManagedVTable *vtbl;
+public:
+ InternalClass *internalClass;
+
+private:
+ friend class MemoryManager;
+ friend struct Identifiers;
+ friend struct ObjectIterator;
+};
+
+// ### Not a good placement
+template<typename T>
+inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; }
+
+
+}
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4math_p.h b/src/qml/qml/v4/qv4math_p.h
new file mode 100644
index 0000000000..a3a3715545
--- /dev/null
+++ b/src/qml/qml/v4/qv4math_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_MATH_H
+#define QMLJS_MATH_H
+
+#include <qglobal.h>
+
+#ifndef QMLJS_LLVM_RUNTIME
+# include <QtCore/qnumeric.h>
+#endif // QMLJS_LLVM_RUNTIME
+#include <cmath>
+
+#if defined(Q_CC_GNU)
+#define QMLJS_READONLY __attribute((const))
+#else
+#define QMLJS_READONLY
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+#if !defined(QMLJS_LLVM_RUNTIME) && defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86)
+
+static inline QMLJS_READONLY Value add_int32(int a, int b)
+{
+ quint8 overflow = 0;
+ int aa = a;
+
+ asm ("addl %2, %1\n"
+ "seto %0"
+ : "=q" (overflow), "=r" (aa)
+ : "r" (b), "1" (aa)
+ : "cc"
+ );
+ if (!overflow)
+ return Value::fromInt32(aa);
+ return Value::fromDouble((double)a + (double)b);
+}
+
+static inline QMLJS_READONLY Value sub_int32(int a, int b)
+{
+ quint8 overflow = 0;
+ int aa = a;
+
+ asm ("subl %2, %1\n"
+ "seto %0"
+ : "=q" (overflow), "=r" (aa)
+ : "r" (b), "1" (aa)
+ : "cc"
+ );
+ if (!overflow)
+ return Value::fromInt32(aa);
+ return Value::fromDouble((double)a - (double)b);
+}
+
+static inline QMLJS_READONLY Value mul_int32(int a, int b)
+{
+ quint8 overflow = 0;
+ int aa = a;
+
+ asm ("imul %2, %1\n"
+ "setc %0"
+ : "=q" (overflow), "=r" (aa)
+ : "r" (b), "1" (aa)
+ : "cc"
+ );
+ if (!overflow)
+ return Value::fromInt32(aa);
+ return Value::fromDouble((double)a * (double)b);
+}
+
+#else
+
+static inline QMLJS_READONLY Value add_int32(int a, int b)
+{
+ qint64 result = a + b;
+ if (result > INT_MAX || result < INT_MIN)
+ return Value::fromDouble(result);
+ return Value::fromInt32(static_cast<int>(result));
+}
+
+static inline QMLJS_READONLY Value sub_int32(int a, int b)
+{
+ qint64 result = a - b;
+ if (result > INT_MAX || result < INT_MIN)
+ return Value::fromDouble(result);
+ return Value::fromInt32(static_cast<int>(result));
+}
+
+static inline QMLJS_READONLY Value mul_int32(int a, int b)
+{
+ qint64 result = a * b;
+ if (result > INT_MAX || result < INT_MIN)
+ return Value::fromDouble(result);
+ return Value::fromInt32(static_cast<int>(result));
+}
+
+#endif // defined(QMLJS_INLINE_MATH)
+
+}
+
+QT_END_NAMESPACE
+
+#ifdef QMLJS_READONLY
+#undef QMLJS_READONLY
+#endif
+
+#endif // QMLJS_MATH_H
diff --git a/src/qml/qml/v4/qv4mathobject.cpp b/src/qml/qml/v4/qv4mathobject.cpp
new file mode 100644
index 0000000000..7aa56f51bd
--- /dev/null
+++ b/src/qml/qml/v4/qv4mathobject.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4mathobject_p.h"
+#include "qv4objectproto_p.h"
+
+#include <cmath>
+#include <qmath.h>
+#include <qnumeric.h>
+
+using namespace QV4;
+
+static const double qt_PI = 2.0 * ::asin(1.0);
+
+MathObject::MathObject(ExecutionContext *ctx)
+ : Object(ctx->engine)
+{
+ type = Type_MathObject;
+ prototype = ctx->engine->objectPrototype;
+
+ defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5)));
+ defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0)));
+
+ defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1);
+ defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1);
+ defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0);
+ defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1);
+ defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2);
+ defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1);
+ defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1);
+ defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1);
+ defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1);
+ defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1);
+ defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2);
+ defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2);
+ defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2);
+ defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0);
+ defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1);
+ defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1);
+ defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1);
+ defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1);
+}
+
+/* copies the sign from y to x and returns the result */
+static double copySign(double x, double y)
+{
+ uchar *xch = (uchar *)&x;
+ uchar *ych = (uchar *)&y;
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80);
+ else
+ xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80);
+ return x;
+}
+
+Value MathObject::method_abs(SimpleCallContext *context)
+{
+ if (!context->argumentCount)
+ return Value::fromDouble(qSNaN());
+
+ if (context->arguments[0].isInteger()) {
+ int i = context->arguments[0].integerValue();
+ return Value::fromInt32(i < 0 ? - i : i);
+ }
+
+ double v = context->arguments[0].toNumber();
+ if (v == 0) // 0 | -0
+ return Value::fromDouble(0);
+
+ return Value::fromDouble(v < 0 ? -v : v);
+}
+
+Value MathObject::method_acos(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : 2;
+ if (v > 1)
+ return Value::fromDouble(qSNaN());
+
+ return Value::fromDouble(::acos(v));
+}
+
+Value MathObject::method_asin(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : 2;
+ if (v > 1)
+ return Value::fromDouble(qSNaN());
+ else
+ return Value::fromDouble(::asin(v));
+}
+
+Value MathObject::method_atan(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ if (v == 0.0)
+ return Value::fromDouble(v);
+ else
+ return Value::fromDouble(::atan(v));
+}
+
+Value MathObject::method_atan2(SimpleCallContext *context)
+{
+ double v1 = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ double v2 = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN();
+
+ if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0))
+ return Value::fromDouble(copySign(0, -1.0));
+
+ if ((v1 == 0.0) && (v2 == 0.0)) {
+ if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) {
+ return Value::fromDouble(qt_PI);
+ } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) {
+ return Value::fromDouble(-qt_PI);
+ }
+ }
+ return Value::fromDouble(::atan2(v1, v2));
+}
+
+Value MathObject::method_ceil(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ if (v < 0.0 && v > -1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(::ceil(v));
+}
+
+Value MathObject::method_cos(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ return Value::fromDouble(::cos(v));
+}
+
+Value MathObject::method_exp(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ if (qIsInf(v)) {
+ if (copySign(1.0, v) == -1.0)
+ return Value::fromDouble(0);
+ else
+ return Value::fromDouble(qInf());
+ } else {
+ return Value::fromDouble(::exp(v));
+ }
+}
+
+Value MathObject::method_floor(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ return Value::fromDouble(::floor(v));
+}
+
+Value MathObject::method_log(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ if (v < 0)
+ return Value::fromDouble(qSNaN());
+ else
+ return Value::fromDouble(::log(v));
+}
+
+Value MathObject::method_max(SimpleCallContext *context)
+{
+ double mx = -qInf();
+ for (unsigned i = 0; i < context->argumentCount; ++i) {
+ double x = context->arguments[i].toNumber();
+ if (x > mx || std::isnan(x))
+ mx = x;
+ }
+ return Value::fromDouble(mx);
+}
+
+Value MathObject::method_min(SimpleCallContext *context)
+{
+ double mx = qInf();
+ for (unsigned i = 0; i < context->argumentCount; ++i) {
+ double x = context->arguments[i].toNumber();
+ if ((x == 0 && mx == x && copySign(1.0, x) == -1.0)
+ || (x < mx) || std::isnan(x)) {
+ mx = x;
+ }
+ }
+ return Value::fromDouble(mx);
+}
+
+Value MathObject::method_pow(SimpleCallContext *context)
+{
+ double x = context->argumentCount > 0 ? context->arguments[0].toNumber() : qSNaN();
+ double y = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN();
+
+ if (std::isnan(y))
+ return Value::fromDouble(qSNaN());
+
+ if (y == 0) {
+ return Value::fromDouble(1);
+ } else if (((x == 1) || (x == -1)) && std::isinf(y)) {
+ return Value::fromDouble(qSNaN());
+ } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) {
+ return Value::fromDouble(qInf());
+ } else if ((x == 0) && copySign(1.0, x) == -1.0) {
+ if (y < 0) {
+ if (::fmod(-y, 2.0) == 1.0)
+ return Value::fromDouble(-qInf());
+ else
+ return Value::fromDouble(qInf());
+ } else if (y > 0) {
+ if (::fmod(y, 2.0) == 1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(0);
+ }
+ }
+
+#ifdef Q_OS_AIX
+ else if (qIsInf(x) && copySign(1.0, x) == -1.0) {
+ if (y > 0) {
+ if (::fmod(y, 2.0) == 1.0)
+ return Value::fromDouble(-qInf());
+ else
+ return Value::fromDouble(qInf());
+ } else if (y < 0) {
+ if (::fmod(-y, 2.0) == 1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(0);
+ }
+ }
+#endif
+ else {
+ return Value::fromDouble(::pow(x, y));
+ }
+ // ###
+ return Value::fromDouble(qSNaN());
+}
+
+Value MathObject::method_random(SimpleCallContext *)
+{
+ return Value::fromDouble(qrand() / (double) RAND_MAX);
+}
+
+Value MathObject::method_round(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ v = copySign(::floor(v + 0.5), v);
+ return Value::fromDouble(v);
+}
+
+Value MathObject::method_sin(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ return Value::fromDouble(::sin(v));
+}
+
+Value MathObject::method_sqrt(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ return Value::fromDouble(::sqrt(v));
+}
+
+Value MathObject::method_tan(SimpleCallContext *context)
+{
+ double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN();
+ if (v == 0.0)
+ return Value::fromDouble(v);
+ else
+ return Value::fromDouble(::tan(v));
+}
+
diff --git a/src/qml/qml/v4/qv4mathobject_p.h b/src/qml/qml/v4/qv4mathobject_p.h
new file mode 100644
index 0000000000..03c36bcc68
--- /dev/null
+++ b/src/qml/qml/v4/qv4mathobject_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4MATHOBJECT_H
+#define QV$MATHOBJECT_H
+
+#include "qv4object_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct MathObject: Object
+{
+ MathObject(ExecutionContext *ctx);
+
+ static Value method_abs(SimpleCallContext *context);
+ static Value method_acos(SimpleCallContext *context);
+ static Value method_asin(SimpleCallContext *context);
+ static Value method_atan(SimpleCallContext *context);
+ static Value method_atan2(SimpleCallContext *context);
+ static Value method_ceil(SimpleCallContext *context);
+ static Value method_cos(SimpleCallContext *context);
+ static Value method_exp(SimpleCallContext *context);
+ static Value method_floor(SimpleCallContext *context);
+ static Value method_log(SimpleCallContext *context);
+ static Value method_max(SimpleCallContext *context);
+ static Value method_min(SimpleCallContext *context);
+ static Value method_pow(SimpleCallContext *context);
+ static Value method_random(SimpleCallContext *context);
+ static Value method_round(SimpleCallContext *context);
+ static Value method_sin(SimpleCallContext *context);
+ static Value method_sqrt(SimpleCallContext *context);
+ static Value method_tan(SimpleCallContext *context);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4/qv4mm.cpp b/src/qml/qml/v4/qv4mm.cpp
new file mode 100644
index 0000000000..0e53a2088f
--- /dev/null
+++ b/src/qml/qml/v4/qv4mm.cpp
@@ -0,0 +1,605 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4engine_p.h"
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4mm_p.h"
+#include "qv4qobjectwrapper_p.h"
+#include <qqmlengine.h>
+#include "PageAllocation.h"
+#include "StdLibExtras.h"
+
+#include <QTime>
+#include <QVector>
+#include <QVector>
+#include <QMap>
+
+#include <iostream>
+#include <cstdlib>
+#include "qv4alloca_p.h"
+
+#ifdef V4_USE_VALGRIND
+#include <valgrind/valgrind.h>
+#include <valgrind/memcheck.h>
+#endif
+
+#if OS(QNX)
+#include <sys/storage.h> // __tls()
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+using namespace WTF;
+
+static const std::size_t CHUNK_SIZE = 1024*32;
+
+struct MemoryManager::Data
+{
+ bool enableGC;
+ bool gcBlocked;
+ bool scribble;
+ bool aggressiveGC;
+ ExecutionEngine *engine;
+ quintptr *stackTop;
+
+ enum { MaxItemSize = 512 };
+ Managed *smallItems[MaxItemSize/16];
+ uint nChunks[MaxItemSize/16];
+ uint availableItems[MaxItemSize/16];
+ uint allocCount[MaxItemSize/16];
+ struct Chunk {
+ PageAllocation memory;
+ int chunkSize;
+ };
+
+ QVector<Chunk> heapChunks;
+ QHash<Managed *, uint> protectedObject;
+
+ // statistics:
+#ifdef DETAILED_MM_STATS
+ QVector<unsigned> allocSizeCounters;
+#endif // DETAILED_MM_STATS
+
+ Data(bool enableGC)
+ : enableGC(enableGC)
+ , gcBlocked(false)
+ , engine(0)
+ , stackTop(0)
+ {
+ memset(smallItems, 0, sizeof(smallItems));
+ memset(nChunks, 0, sizeof(nChunks));
+ memset(availableItems, 0, sizeof(availableItems));
+ memset(allocCount, 0, sizeof(allocCount));
+ scribble = !qgetenv("MM_SCRIBBLE").isEmpty();
+ aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty();
+ }
+
+ ~Data()
+ {
+ for (QVector<Chunk>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i)
+ i->memory.deallocate();
+ }
+};
+
+#define SCRIBBLE(obj, c, size) \
+ if (m_d->scribble) \
+ ::memset((void *)(obj + 1), c, size - sizeof(Managed));
+
+
+namespace QV4 {
+
+bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b)
+{
+ return a.memory.base() < b.memory.base();
+}
+
+} // namespace QV4
+
+MemoryManager::MemoryManager()
+ : m_d(new Data(true))
+ , m_contextList(0)
+ , m_persistentValues(0)
+ , m_weakValues(0)
+{
+ setEnableGC(true);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_CREATE_MEMPOOL(this, 0, true);
+#endif
+
+#if OS(QNX)
+ // TLS is at the top of each thread's stack,
+ // so the stack base for thread is the result of __tls()
+ m_d->stackTop = reinterpret_cast<quintptr *>(
+ (((uintptr_t)__tls() + __PAGESIZE - 1) & ~(__PAGESIZE - 1)));
+#elif USE(PTHREADS)
+# if OS(DARWIN)
+ void *st = pthread_get_stackaddr_np(pthread_self());
+ m_d->stackTop = static_cast<quintptr *>(st);
+# else
+ void* stackBottom = 0;
+ pthread_attr_t attr;
+ pthread_getattr_np(pthread_self(), &attr);
+ size_t stackSize = 0;
+ pthread_attr_getstack(&attr, &stackBottom, &stackSize);
+ pthread_attr_destroy(&attr);
+
+ m_d->stackTop = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr);
+# endif
+#elif OS(WINDOWS)
+ PNT_TIB tib = (PNT_TIB)NtCurrentTeb();
+ m_d->stackTop = static_cast<quintptr*>(tib->StackBase);
+#else
+# error "Unsupported platform: no way to get the top-of-stack."
+#endif
+
+}
+
+Managed *MemoryManager::alloc(std::size_t size)
+{
+ if (m_d->aggressiveGC)
+ runGC();
+#ifdef DETAILED_MM_STATS
+ willAllocate(size);
+#endif // DETAILED_MM_STATS
+
+ assert(size >= 16);
+ assert(size % 16 == 0);
+
+ size_t pos = size >> 4;
+ ++m_d->allocCount[pos];
+
+ // fits into a small bucket
+ assert(size < MemoryManager::Data::MaxItemSize);
+
+ Managed *m = m_d->smallItems[pos];
+ if (m)
+ goto found;
+
+ // try to free up space, otherwise allocate
+ if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) {
+ runGC();
+ m = m_d->smallItems[pos];
+ if (m)
+ goto found;
+ }
+
+ // no free item available, allocate a new chunk
+ {
+ // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks
+ uint shift = ++m_d->nChunks[pos];
+ if (shift > 10)
+ shift = 10;
+ std::size_t allocSize = CHUNK_SIZE*(1 << shift);
+ allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize);
+ Data::Chunk allocation;
+ allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages);
+ allocation.chunkSize = size;
+ m_d->heapChunks.append(allocation);
+ qSort(m_d->heapChunks);
+ char *chunk = (char *)allocation.memory.base();
+ char *end = chunk + allocation.memory.size() - size;
+ memset(chunk, 0, allocation.memory.size());
+ Managed **last = &m_d->smallItems[pos];
+ while (chunk <= end) {
+ Managed *o = reinterpret_cast<Managed *>(chunk);
+ o->_data = 0;
+ *last = o;
+ last = o->nextFreeRef();
+ chunk += size;
+ }
+ *last = 0;
+ m = m_d->smallItems[pos];
+ m_d->availableItems[pos] += allocation.memory.size()/size - 1;
+#ifdef V4_USE_VALGRIND
+ VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize);
+#endif
+ }
+
+ found:
+#ifdef V4_USE_VALGRIND
+ VALGRIND_MEMPOOL_ALLOC(this, m, size);
+#endif
+
+ m_d->smallItems[pos] = m->nextFree();
+ return m;
+}
+
+void MemoryManager::mark()
+{
+ m_d->engine->markObjects();
+
+ for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it)
+ it.key()->mark();
+
+ PersistentValuePrivate *persistent = m_persistentValues;
+ while (persistent) {
+ if (!persistent->refcount) {
+ PersistentValuePrivate *n = persistent->next;
+ persistent->removeFromList();
+ delete persistent;
+ persistent = n;
+ continue;
+ }
+ if (Managed *m = persistent->value.asManaged())
+ m->mark();
+ persistent = persistent->next;
+ }
+
+ // push all caller saved registers to the stack, so we can find the objects living in these registers
+#if COMPILER(MSVC)
+# if CPU(X86_64)
+ HANDLE thread = GetCurrentThread();
+ WOW64_CONTEXT ctxt;
+ /*bool success =*/ Wow64GetThreadContext(thread, &ctxt);
+# elif CPU(X86)
+ HANDLE thread = GetCurrentThread();
+ CONTEXT ctxt;
+ /*bool success =*/ GetThreadContext(thread, &ctxt);
+# endif // CPU
+#elif COMPILER(CLANG) || COMPILER(GCC)
+# if CPU(X86_64)
+ quintptr regs[5];
+ asm(
+ "mov %%rbp, %0\n"
+ "mov %%r12, %1\n"
+ "mov %%r13, %2\n"
+ "mov %%r14, %3\n"
+ "mov %%r15, %4\n"
+ : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4])
+ :
+ :
+ );
+# endif // CPU
+#endif // COMPILER
+
+ collectFromStack();
+
+ // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership
+ // keeps all of its children alive in JavaScript.
+
+ // Do this _after_ collectFromStack to ensure that processing the weak
+ // managed objects in the loop down there doesn't make then end up as leftovers
+ // on the stack and thus always get collected.
+ for (PersistentValuePrivate *weak = m_weakValues; weak; weak = weak->next) {
+ if (!weak->refcount)
+ continue;
+ QObjectWrapper *qobjectWrapper = weak->value.as<QObjectWrapper>();
+ if (!qobjectWrapper)
+ continue;
+ QObject *qobject = qobjectWrapper->object();
+ if (!qobject)
+ continue;
+ bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject);
+
+ if (!keepAlive) {
+ if (QObject *parent = qobject->parent()) {
+ while (parent->parent())
+ parent = parent->parent();
+
+ keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent);
+ }
+ }
+
+ if (keepAlive)
+ qobjectWrapper->mark();
+ }
+}
+
+std::size_t MemoryManager::sweep(bool lastSweep)
+{
+ PersistentValuePrivate *weak = m_weakValues;
+ while (weak) {
+ if (!weak->refcount) {
+ PersistentValuePrivate *n = weak->next;
+ weak->removeFromList();
+ delete weak;
+ weak = n;
+ continue;
+ }
+ if (Managed *m = weak->value.asManaged()) {
+ if (!m->markBit) {
+ weak->value = Value::emptyValue();
+ PersistentValuePrivate *n = weak->next;
+ weak->removeFromList();
+ weak = n;
+ continue;
+ }
+ }
+ weak = weak->next;
+ }
+
+ if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) {
+ for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
+ if (!it.value()->markBit)
+ it = multiplyWrappedQObjects->erase(it);
+ else
+ ++it;
+ }
+ }
+
+ std::size_t freedCount = 0;
+ GCDeletable *deletable = 0;
+ GCDeletable **firstDeletable = &deletable;
+
+ for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i)
+ freedCount += sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize, &deletable);
+
+ ExecutionContext *ctx = m_contextList;
+ ExecutionContext **n = &m_contextList;
+ while (ctx) {
+ ExecutionContext *next = ctx->next;
+ if (!ctx->marked) {
+ free(ctx);
+ *n = next;
+ } else {
+ ctx->marked = false;
+ n = &ctx->next;
+ }
+ ctx = next;
+ }
+
+ deletable = *firstDeletable;
+ while (deletable) {
+ GCDeletable *next = deletable->next;
+ deletable->lastCall = lastSweep;
+ delete deletable;
+ deletable = next;
+ }
+
+ return freedCount;
+}
+
+std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable)
+{
+// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]);
+ std::size_t freedCount = 0;
+
+ Managed **f = &m_d->smallItems[size >> 4];
+
+#ifdef V4_USE_VALGRIND
+ VALGRIND_DISABLE_ERROR_REPORTING;
+#endif
+ for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) {
+ Managed *m = reinterpret_cast<Managed *>(chunk);
+// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s",
+// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false"));
+
+ assert((intptr_t) chunk % 16 == 0);
+
+ if (m->inUse) {
+ if (m->markBit) {
+ m->markBit = 0;
+ } else {
+// qDebug() << "-- collecting it." << m << *f << m->nextFree();
+#ifdef V4_USE_VALGRIND
+ VALGRIND_ENABLE_ERROR_REPORTING;
+#endif
+ if (m->vtbl->collectDeletables)
+ m->vtbl->collectDeletables(m, deletable);
+ m->vtbl->destroy(m);
+
+ m->setNextFree(*f);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_DISABLE_ERROR_REPORTING;
+ VALGRIND_MEMPOOL_FREE(this, m);
+#endif
+ *f = m;
+ SCRIBBLE(m, 0x99, size);
+ ++freedCount;
+ }
+ }
+ }
+#ifdef V4_USE_VALGRIND
+ VALGRIND_ENABLE_ERROR_REPORTING;
+#endif
+
+ return freedCount;
+}
+
+bool MemoryManager::isGCBlocked() const
+{
+ return m_d->gcBlocked;
+}
+
+void MemoryManager::setGCBlocked(bool blockGC)
+{
+ m_d->gcBlocked = blockGC;
+}
+
+void MemoryManager::runGC()
+{
+ if (!m_d->enableGC || m_d->gcBlocked) {
+// qDebug() << "Not running GC.";
+ return;
+ }
+
+// QTime t; t.start();
+
+// qDebug() << ">>>>>>>>runGC";
+
+ mark();
+// std::cerr << "GC: marked " << marks
+// << " objects in " << t.elapsed()
+// << "ms" << std::endl;
+
+// t.restart();
+ /*std::size_t freedCount =*/ sweep();
+// std::cerr << "GC: sweep freed " << freedCount
+// << " objects in " << t.elapsed()
+// << "ms" << std::endl;
+ memset(m_d->allocCount, 0, sizeof(m_d->allocCount));
+}
+
+void MemoryManager::setEnableGC(bool enableGC)
+{
+ m_d->enableGC = enableGC;
+}
+
+MemoryManager::~MemoryManager()
+{
+ PersistentValuePrivate *persistent = m_persistentValues;
+ while (persistent) {
+ PersistentValuePrivate *n = persistent->next;
+ persistent->value = Value::undefinedValue();
+ persistent->engine = 0;
+ persistent->prev = 0;
+ persistent->next = 0;
+ persistent = n;
+ }
+
+ sweep(/*lastSweep*/true);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL(this);
+#endif
+}
+
+void MemoryManager::protect(Managed *m)
+{
+ ++m_d->protectedObject[m];
+}
+
+void MemoryManager::unprotect(Managed *m)
+{
+ if (!--m_d->protectedObject[m])
+ m_d->protectedObject.remove(m);
+}
+
+static inline void add(QVector<Managed *> &values, const Value &v)
+{
+ if (Object *o = v.asObject())
+ values.append(o);
+}
+
+void MemoryManager::setExecutionEngine(ExecutionEngine *engine)
+{
+ m_d->engine = engine;
+}
+
+void MemoryManager::dumpStats() const
+{
+#ifdef DETAILED_MM_STATS
+ std::cerr << "=================" << std::endl;
+ std::cerr << "Allocation stats:" << std::endl;
+ std::cerr << "Requests for each chunk size:" << std::endl;
+ for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) {
+ if (unsigned count = m_d->allocSizeCounters[i]) {
+ std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl;
+ }
+ }
+#endif // DETAILED_MM_STATS
+}
+
+ExecutionEngine *MemoryManager::engine() const
+{
+ return m_d->engine;
+}
+
+#ifdef DETAILED_MM_STATS
+void MemoryManager::willAllocate(std::size_t size)
+{
+ unsigned alignedSize = (size + 15) >> 4;
+ QVector<unsigned> &counters = m_d->allocSizeCounters;
+ if ((unsigned) counters.size() < alignedSize + 1)
+ counters.resize(alignedSize + 1);
+ counters[alignedSize]++;
+}
+
+#endif // DETAILED_MM_STATS
+
+void MemoryManager::collectFromStack() const
+{
+ quintptr valueOnStack = 0;
+
+ if (!m_d->heapChunks.count())
+ return;
+
+ quintptr *current = (&valueOnStack) + 1;
+// qDebug() << "collectFromStack";// << top << current << &valueOnStack;
+
+#if V4_USE_VALGRIND
+ VALGRIND_MAKE_MEM_DEFINED(current, (m_d->stackTop - current)*sizeof(quintptr));
+#endif
+
+ char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*));
+ char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count();
+ int i = 0;
+ for (QVector<Data::Chunk>::Iterator it = m_d->heapChunks.begin(), end =
+ m_d->heapChunks.end(); it != end; ++it) {
+ heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) - 1;
+ heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) + it->memory.size() - it->chunkSize;
+ }
+ assert(i == m_d->heapChunks.count() * 2);
+
+ for (; current < m_d->stackTop; ++current) {
+ char* genericPtr =
+#if QT_POINTER_SIZE == 8
+ reinterpret_cast<char *>((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift));
+#else
+ reinterpret_cast<char *>(*current);
+#endif
+
+ if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1))
+ continue;
+ int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries;
+ // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid.
+ assert(index >= 0 && index < m_d->heapChunks.count() * 2);
+ if (index & 1) {
+ int size = m_d->heapChunks.at(index >> 1).chunkSize;
+ Managed *m = reinterpret_cast<Managed *>(genericPtr);
+// qDebug() << " inside" << size;
+
+ if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1] - 1 ) % size)
+ // wrongly aligned value, skip it
+ continue;
+
+ if (!m->inUse)
+ // Skip pointers to already freed objects, they are bogus as well
+ continue;
+
+// qDebug() << " marking";
+ m->mark();
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4mm_p.h b/src/qml/qml/v4/qv4mm_p.h
new file mode 100644
index 0000000000..f72d23dc9a
--- /dev/null
+++ b/src/qml/qml/v4/qv4mm_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GC_H
+#define QV4GC_H
+
+#include "qv4global_p.h"
+#include "qv4context_p.h"
+
+#include <QScopedPointer>
+
+//#define DETAILED_MM_STATS
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ExecutionEngine;
+struct ExecutionContext;
+struct Managed;
+struct GCDeletable;
+
+class Q_QML_EXPORT MemoryManager
+{
+ MemoryManager(const MemoryManager &);
+ MemoryManager &operator=(const MemoryManager&);
+
+public:
+ struct Data;
+
+ class GCBlocker
+ {
+ public:
+ GCBlocker(MemoryManager *mm)
+ : mm(mm)
+ , wasBlocked(mm->isGCBlocked())
+ {
+ mm->setGCBlocked(true);
+ }
+
+ ~GCBlocker()
+ {
+ mm->setGCBlocked(wasBlocked);
+ }
+
+ private:
+ MemoryManager *mm;
+ bool wasBlocked;
+ };
+
+public:
+ MemoryManager();
+ ~MemoryManager();
+
+ void protect(Managed *m);
+ void unprotect(Managed *m);
+
+ // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries).
+ // Note: all occurances of "16" in alloc/dealloc are also due to the alignment.
+ static inline std::size_t align(std::size_t size)
+ { return (size + 15) & ~0xf; }
+
+ inline Managed *allocManaged(std::size_t size)
+ {
+ size = align(size);
+ Managed *o = alloc(size);
+ return o;
+ }
+
+ ExecutionContext *allocContext(uint size);
+
+ bool isGCBlocked() const;
+ void setGCBlocked(bool blockGC);
+ void runGC();
+
+ void setEnableGC(bool enableGC);
+ void setExecutionEngine(ExecutionEngine *engine);
+
+ void dumpStats() const;
+
+protected:
+ /// expects size to be aligned
+ // TODO: try to inline
+ Managed *alloc(std::size_t size);
+
+ ExecutionEngine *engine() const;
+
+#ifdef DETAILED_MM_STATS
+ void willAllocate(std::size_t size);
+#endif // DETAILED_MM_STATS
+
+private:
+ void collectFromStack() const;
+ void mark();
+ std::size_t sweep(bool lastSweep = false);
+ std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable);
+
+protected:
+ QScopedPointer<Data> m_d;
+ ExecutionContext *m_contextList;
+public:
+ PersistentValuePrivate *m_persistentValues;
+ PersistentValuePrivate *m_weakValues;
+};
+
+inline ExecutionContext *MemoryManager::allocContext(uint size)
+{
+ ExecutionContext *newContext = (ExecutionContext *)malloc(size);
+ newContext->next = m_contextList;
+ m_contextList = newContext;
+ return newContext;
+}
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GC_H
diff --git a/src/qml/qml/v4/qv4numberobject.cpp b/src/qml/qml/v4/qv4numberobject.cpp
new file mode 100644
index 0000000000..266fa792dc
--- /dev/null
+++ b/src/qml/qml/v4/qv4numberobject.cpp
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4numberobject_p.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDebug>
+#include <cassert>
+#include <double-conversion.h>
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(NumberCtor);
+
+NumberCtor::NumberCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Number")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value NumberCtor::construct(Managed *m, Value *args, int argc)
+{
+ double d = argc ? args[0].toNumber() : 0.;
+ return Value::fromObject(m->engine()->newNumberObject(Value::fromDouble(d)));
+}
+
+Value NumberCtor::call(Managed *, const Value &, Value *argv, int argc)
+{
+ double d = argc ? argv[0].toNumber() : 0.;
+ return Value::fromDouble(d);
+}
+
+void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN()));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf()));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf()));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308));
+
+#ifdef __INTEL_COMPILER
+# pragma warning( push )
+# pragma warning(disable: 239)
+#endif
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324));
+#ifdef __INTEL_COMPILER
+# pragma warning( pop )
+#endif
+
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf);
+ defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1);
+ defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential);
+ defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision);
+}
+
+Value NumberPrototype::method_toString(SimpleCallContext *ctx)
+{
+ double num;
+ if (ctx->thisObject.isNumber()) {
+ num = ctx->thisObject.asDouble();
+ } else {
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+ num = thisObject->value.asDouble();
+ }
+
+ Value arg = ctx->argument(0);
+ if (!arg.isUndefined()) {
+ int radix = arg.toInt32();
+ if (radix < 2 || radix > 36) {
+ ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix")
+ .arg(radix));
+ return Value::undefinedValue();
+ }
+
+ if (std::isnan(num)) {
+ return Value::fromString(ctx, QStringLiteral("NaN"));
+ } else if (qIsInf(num)) {
+ return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity"));
+ }
+
+ if (radix != 10) {
+ QString str;
+ bool negative = false;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+ double frac = num - ::floor(num);
+ num = Value::toInteger(num);
+ do {
+ char c = (char)::fmod(num, radix);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ str.prepend(QLatin1Char(c));
+ num = ::floor(num / radix);
+ } while (num != 0);
+ if (frac != 0) {
+ str.append(QLatin1Char('.'));
+ do {
+ frac = frac * radix;
+ char c = (char)::floor(frac);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ str.append(QLatin1Char(c));
+ frac = frac - ::floor(frac);
+ } while (frac != 0);
+ }
+ if (negative)
+ str.prepend(QLatin1Char('-'));
+ return Value::fromString(ctx, str);
+ }
+ }
+
+ String *str = Value::fromDouble(num).toString(ctx);
+ return Value::fromString(str);
+}
+
+Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ String *str = thisObject->value.toString(ctx);
+ return Value::fromString(str);
+}
+
+Value NumberPrototype::method_valueOf(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ return thisObject->value;
+}
+
+Value NumberPrototype::method_toFixed(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger();
+
+ if (std::isnan(fdigits))
+ fdigits = 0;
+
+ if (fdigits < 0 || fdigits > 20)
+ ctx->throwRangeError(ctx->thisObject);
+
+ double v = thisObject->value.asDouble();
+ QString str;
+ if (std::isnan(v))
+ str = QString::fromLatin1("NaN");
+ else if (qIsInf(v))
+ str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity");
+ else if (v < 1.e21)
+ str = QString::number(v, 'f', int (fdigits));
+ else
+ return __qmljs_string_from_number(ctx, v);
+ return Value::fromString(ctx, str);
+}
+
+Value NumberPrototype::method_toExponential(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ Value fraction = ctx->argument(0);
+ int fdigits = -1;
+
+ if (!fraction.isUndefined()) {
+ int fdigits = ctx->argument(0).toInt32();
+ if (fdigits < 0 || fdigits > 20) {
+ String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"));
+ ctx->throwRangeError(Value::fromString(error));
+ }
+ }
+
+ char str[100];
+ double_conversion::StringBuilder builder(str, sizeof(str));
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(thisObject->value.asDouble(), fdigits, &builder);
+ QString result = QString::fromLatin1(builder.Finalize());
+
+ return Value::fromString(ctx, result);
+}
+
+Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ Value prec = ctx->argument(0);
+ if (prec.isUndefined())
+ return __qmljs_to_string(thisObject->value, ctx);
+
+ double precision = prec.toInt32();
+ if (precision < 1 || precision > 21) {
+ String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range"));
+ ctx->throwRangeError(Value::fromString(error));
+ }
+
+ char str[100];
+ double_conversion::StringBuilder builder(str, sizeof(str));
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(thisObject->value.asDouble(), precision, &builder);
+ QString result = QString::fromLatin1(builder.Finalize());
+
+ return Value::fromString(ctx, result);
+}
diff --git a/src/qml/qml/v4/qv4numberobject_p.h b/src/qml/qml/v4/qv4numberobject_p.h
new file mode 100644
index 0000000000..0c06451c98
--- /dev/null
+++ b/src/qml/qml/v4/qv4numberobject_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4NUMBEROBJECT_H
+#define QV4NUMBEROBJECT_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct NumberCtor: FunctionObject
+{
+ NumberCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, Value *args, int argc);
+ static Value call(Managed *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct NumberPrototype: NumberObject
+{
+ NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_toLocaleString(SimpleCallContext *ctx);
+ static Value method_valueOf(SimpleCallContext *ctx);
+ static Value method_toFixed(SimpleCallContext *ctx);
+ static Value method_toExponential(SimpleCallContext *ctx);
+ static Value method_toPrecision(SimpleCallContext *ctx);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4object.cpp b/src/qml/qml/v4/qv4object.cpp
new file mode 100644
index 0000000000..16b57871af
--- /dev/null
+++ b/src/qml/qml/v4/qv4object.cpp
@@ -0,0 +1,1383 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4object_p.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4stringobject_p.h"
+#include "qv4argumentsobject_p.h"
+#include "qv4mm_p.h"
+#include "qv4lookup_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/qmath.h>
+#include <QtCore/QDebug>
+#include <cassert>
+#include <typeinfo>
+#include <iostream>
+#include <stdint.h>
+#include "qv4alloca_p.h"
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(Object);
+
+Object::Object(ExecutionEngine *engine)
+ : Managed(engine->emptyClass)
+ , prototype(0)
+ , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+ , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+{
+ vtbl = &static_vtbl;
+ type = Type_Object;
+ memset(memberData, 0, sizeof(Property)*memberDataAlloc);
+}
+
+Object::Object(ExecutionContext *context)
+ : Managed(context->engine->emptyClass)
+ , prototype(0)
+ , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+ , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+{
+ vtbl = &static_vtbl;
+ type = Type_Object;
+ memset(memberData, 0, sizeof(Property)*memberDataAlloc);
+}
+
+Object::Object(ExecutionEngine *engine, InternalClass *internalClass)
+ : Managed(internalClass)
+ , prototype(0)
+ , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+ , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+{
+ vtbl = &static_vtbl;
+ type = Type_Object;
+
+ if (internalClass->size >= memberDataAlloc) {
+ memberDataAlloc = internalClass->size;
+ memberData = new Property[memberDataAlloc];
+ }
+ memset(memberData, 0, sizeof(Property)*memberDataAlloc);
+}
+
+Object::~Object()
+{
+ if (memberData != inlineProperties)
+ delete [] memberData;
+ delete [] (arrayData - (sparseArray ? 0 : arrayOffset));
+ if (arrayAttributes)
+ delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset));
+ delete sparseArray;
+ _data = 0;
+}
+
+void Object::destroy(Managed *that)
+{
+ static_cast<Object *>(that)->~Object();
+}
+
+void Object::put(ExecutionContext *ctx, const QString &name, const Value &value)
+{
+ put(ctx->engine->newString(name), value);
+}
+
+Value Object::getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs)
+{
+ if (!attrs.isAccessor())
+ return p->value;
+ FunctionObject *getter = p->getter();
+ if (!getter)
+ return Value::undefinedValue();
+
+ return getter->call(thisObject, 0, 0);
+}
+
+void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value)
+{
+ if (attrs.isAccessor()) {
+ if (pd->set) {
+ Value args[1];
+ args[0] = value;
+ pd->set->call(Value::fromObject(this), args, 1);
+ return;
+ }
+ goto reject;
+ }
+
+ if (!attrs.isWritable())
+ goto reject;
+
+ pd->value = value;
+ return;
+
+ reject:
+ if (engine()->current->strictMode)
+ engine()->current->throwTypeError();
+
+}
+
+void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs)
+{
+ Value v = get(name);
+ Value result;
+ op(ctx, &result, v, rhs);
+ put(name, result);
+}
+
+void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs)
+{
+ uint idx = index.asArrayIndex();
+ if (idx < UINT_MAX) {
+ bool hasProperty = false;
+ Value v = getIndexed(idx, &hasProperty);
+ Value result;
+ op(ctx, &result, v, rhs);
+ putIndexed(idx, result);
+ return;
+ }
+ String *name = index.toString(ctx);
+ assert(name);
+ inplaceBinOp(ctx, op, name, rhs);
+}
+
+void Object::defineDefaultProperty(String *name, Value value)
+{
+ Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable);
+ pd->value = value;
+}
+
+void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value)
+{
+ defineDefaultProperty(context->engine->newIdentifier(name), value);
+}
+
+void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value)
+{
+ defineDefaultProperty(engine->newIdentifier(name), value);
+}
+
+void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount)
+{
+ Q_UNUSED(argumentCount);
+ String *s = context->engine->newIdentifier(name);
+ FunctionObject* function = context->engine->newBuiltinFunction(context, s, code);
+ function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount));
+ defineDefaultProperty(s, Value::fromObject(function));
+}
+
+void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount)
+{
+ Q_UNUSED(argumentCount);
+ String *s = engine->newIdentifier(name);
+ FunctionObject* function = engine->newBuiltinFunction(engine->rootContext, s, code);
+ function->defineReadonlyProperty(engine->id_length, Value::fromInt32(argumentCount));
+ defineDefaultProperty(s, Value::fromObject(function));
+}
+
+void Object::defineAccessorProperty(ExecutionEngine *engine, const QString &name,
+ Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *))
+{
+ String *s = engine->newString(name);
+ defineAccessorProperty(s, getter, setter);
+}
+
+void Object::defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *))
+{
+ ExecutionEngine *v4 = engine();
+ Property *p = insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
+ if (getter)
+ p->setGetter(v4->newBuiltinFunction(v4->rootContext, name, getter));
+ if (setter)
+ p->setSetter(v4->newBuiltinFunction(v4->rootContext, name, setter));
+}
+
+void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value)
+{
+ defineReadonlyProperty(engine->newIdentifier(name), value);
+}
+
+void Object::defineReadonlyProperty(String *name, Value value)
+{
+ Property *pd = insertMember(name, Attr_ReadOnly);
+ pd->value = value;
+}
+
+void Object::markObjects(Managed *that)
+{
+ Object *o = static_cast<Object *>(that);
+ if (o->prototype)
+ o->prototype->mark();
+
+ for (int i = 0; i < o->internalClass->size; ++i) {
+ const Property &pd = o->memberData[i];
+ if (o->internalClass->propertyData[i].isData()) {
+ if (Managed *m = pd.value.asManaged())
+ m->mark();
+ } else {
+ if (pd.getter())
+ pd.getter()->mark();
+ if (pd.setter())
+ pd.setter()->mark();
+ }
+ }
+ o->markArrayObjects();
+}
+
+Property *Object::insertMember(String *s, PropertyAttributes attributes)
+{
+ uint idx;
+ internalClass = internalClass->addMember(s, attributes, &idx);
+
+ if (idx >= memberDataAlloc) {
+ memberDataAlloc = qMax((uint)8, 2*memberDataAlloc);
+ Property *newMemberData = new Property[memberDataAlloc];
+ memcpy(newMemberData, memberData, sizeof(Property)*idx);
+ memset(newMemberData + idx, 0, sizeof(Property)*(memberDataAlloc - idx));
+ if (memberData != inlineProperties)
+ delete [] memberData;
+ memberData = newMemberData;
+ }
+ return memberData + idx;
+}
+
+// Section 8.12.1
+Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return __getOwnProperty__(idx, attrs);
+
+ uint member = internalClass->find(name);
+ if (member < UINT_MAX) {
+ if (attrs)
+ *attrs = internalClass->propertyData[member];
+ return memberData + member;
+ }
+
+ if (attrs)
+ *attrs = Attr_Invalid;
+ return 0;
+}
+
+Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs)
+{
+ uint pidx = propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX) {
+ Property *p = arrayData + pidx;
+ if (!arrayAttributes || arrayAttributes[pidx].isData()) {
+ if (attrs)
+ *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ return p;
+ } else if (arrayAttributes[pidx].isAccessor()) {
+ if (attrs)
+ *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor);
+ return p;
+ }
+ }
+ if (isStringObject()) {
+ if (attrs)
+ *attrs = Attr_NotConfigurable|Attr_NotWritable;
+ return static_cast<StringObject *>(this)->getIndex(index);
+ }
+
+ if (attrs)
+ *attrs = Attr_Invalid;
+ return 0;
+}
+
+// Section 8.12.2
+Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return __getPropertyDescriptor__(idx);
+
+
+ const Object *o = this;
+ while (o) {
+ uint idx = o->internalClass->find(name);
+ if (idx < UINT_MAX) {
+ if (attrs)
+ *attrs = o->internalClass->propertyData[idx];
+ return o->memberData + idx;
+ }
+
+ o = o->prototype;
+ }
+ if (attrs)
+ *attrs = Attr_Invalid;
+ return 0;
+}
+
+Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const
+{
+ const Object *o = this;
+ while (o) {
+ uint pidx = o->propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX) {
+ Property *p = o->arrayData + pidx;
+ if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) {
+ if (attrs)
+ *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ return p;
+ }
+ }
+ if (o->isStringObject()) {
+ Property *p = static_cast<const StringObject *>(o)->getIndex(index);
+ if (p) {
+ if (attrs)
+ *attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ return p;
+ }
+ }
+ o = o->prototype;
+ }
+ if (attrs)
+ *attrs = Attr_Invalid;
+ return 0;
+}
+
+bool Object::__hasProperty__(String *name) const
+{
+ if (__getPropertyDescriptor__(name))
+ return true;
+ return !query(name).isEmpty();
+}
+
+Value Object::get(Managed *m, String *name, bool *hasProperty)
+{
+ return static_cast<Object *>(m)->internalGet(name, hasProperty);
+}
+
+Value Object::getIndexed(Managed *m, uint index, bool *hasProperty)
+{
+ return static_cast<Object *>(m)->internalGetIndexed(index, hasProperty);
+}
+
+void Object::put(Managed *m, String *name, const Value &value)
+{
+ static_cast<Object *>(m)->internalPut(name, value);
+}
+
+void Object::putIndexed(Managed *m, uint index, const Value &value)
+{
+ static_cast<Object *>(m)->internalPutIndexed(index, value);
+}
+
+PropertyAttributes Object::query(const Managed *m, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return queryIndexed(m, idx);
+
+ const Object *o = static_cast<const Object *>(m);
+ while (o) {
+ uint idx = o->internalClass->find(name);
+ if (idx < UINT_MAX)
+ return o->internalClass->propertyData[idx];
+
+ o = o->prototype;
+ }
+ return Attr_Invalid;
+}
+
+PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
+{
+ const Object *o = static_cast<const Object *>(m);
+ while (o) {
+ uint pidx = o->propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX) {
+ if (o->arrayAttributes)
+ return o->arrayAttributes[pidx];
+ return Attr_Data;
+ }
+ if (o->isStringObject()) {
+ Property *p = static_cast<const StringObject *>(o)->getIndex(index);
+ if (p)
+ return Attr_Data;
+ }
+ o = o->prototype;
+ }
+ return Attr_Invalid;
+}
+
+bool Object::deleteProperty(Managed *m, String *name)
+{
+ return static_cast<Object *>(m)->internalDeleteProperty(name);
+}
+
+bool Object::deleteIndexedProperty(Managed *m, uint index)
+{
+ return static_cast<Object *>(m)->internalDeleteIndexedProperty(index);
+}
+
+void Object::getLookup(Managed *m, Lookup *l, Value *result)
+{
+ Object *o = static_cast<Object *>(m);
+ PropertyAttributes attrs;
+ Property *p = l->lookup(o, &attrs);
+ if (p) {
+ if (attrs.isData()) {
+ if (l->level == 0)
+ l->getter = Lookup::getter0;
+ else if (l->level == 1)
+ l->getter = Lookup::getter1;
+ else if (l->level == 2)
+ l->getter = Lookup::getter2;
+ if (result)
+ *result = p->value;
+ return;
+ } else {
+ if (l->level == 0)
+ l->getter = Lookup::getterAccessor0;
+ else if (l->level == 1)
+ l->getter = Lookup::getterAccessor1;
+ else if (l->level == 2)
+ l->getter = Lookup::getterAccessor2;
+ if (result)
+ *result = p->value;
+ Value res = o->getValue(p, attrs);
+ if (result)
+ *result = res;
+ return;
+ }
+ } else if (result) {
+ *result = Value::undefinedValue();
+ }
+}
+
+void Object::setLookup(Managed *m, Lookup *l, const Value &value)
+{
+ Object *o = static_cast<Object *>(m);
+
+ uint idx = o->internalClass->find(l->name);
+ if (!o->isArrayObject() || idx != ArrayObject::LengthPropertyIndex) {
+ if (idx != UINT_MAX && o->internalClass->propertyData[idx].isData() && o->internalClass->propertyData[idx].isWritable()) {
+ l->classList[0] = o->internalClass;
+ l->index = idx;
+ l->setter = Lookup::setter0;
+ o->memberData[idx].value = value;
+ return;
+ }
+
+ if (idx != UINT_MAX) {
+ o->putValue(o->memberData + idx, o->internalClass->propertyData[idx], value);
+ return;
+ }
+ }
+
+ o->put(l->name, value);
+}
+
+Property *Object::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs)
+{
+ Object *o = static_cast<Object *>(m);
+ *name = 0;
+ *index = UINT_MAX;
+
+ if (!it->arrayIndex)
+ it->arrayNode = o->sparseArrayBegin();
+
+ // sparse arrays
+ if (it->arrayNode) {
+ while (it->arrayNode != o->sparseArrayEnd()) {
+ int k = it->arrayNode->key();
+ uint pidx = it->arrayNode->value;
+ Property *p = o->arrayData + pidx;
+ it->arrayNode = it->arrayNode->nextNode();
+ PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
+ it->arrayIndex = k + 1;
+ *index = k;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+ it->arrayNode = 0;
+ it->arrayIndex = UINT_MAX;
+ }
+ // dense arrays
+ while (it->arrayIndex < o->arrayDataLen) {
+ uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex);
+ Property *p = o->arrayData + pidx;
+ PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ ++it->arrayIndex;
+ if ((!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric())
+ && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
+ *index = it->arrayIndex - 1;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+
+ while (it->memberIndex < o->internalClass->size) {
+ String *n = o->internalClass->nameMap.at(it->memberIndex);
+ assert(n);
+
+ Property *p = o->memberData + it->memberIndex;
+ PropertyAttributes a = o->internalClass->propertyData[it->memberIndex];
+ ++it->memberIndex;
+ if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
+ *name = n;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+
+ return 0;
+}
+
+// Section 8.12.3
+Value Object::internalGet(String *name, bool *hasProperty)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return getIndexed(idx, hasProperty);
+
+ name->makeIdentifier();
+
+ Object *o = this;
+ while (o) {
+ uint idx = o->internalClass->find(name);
+ if (idx < UINT_MAX) {
+ if (hasProperty)
+ *hasProperty = true;
+ return getValue(o->memberData + idx, o->internalClass->propertyData.at(idx));
+ }
+
+ o = o->prototype;
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+Value Object::internalGetIndexed(uint index, bool *hasProperty)
+{
+ Property *pd = 0;
+ PropertyAttributes attrs = Attr_Data;
+ Object *o = this;
+ while (o) {
+ uint pidx = o->propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX) {
+ if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) {
+ pd = o->arrayData + pidx;
+ if (o->arrayAttributes)
+ attrs = o->arrayAttributes[pidx];
+ break;
+ }
+ }
+ if (o->isStringObject()) {
+ pd = static_cast<StringObject *>(o)->getIndex(index);
+ if (pd) {
+ attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ break;
+ }
+ }
+ o = o->prototype;
+ }
+
+ if (pd) {
+ if (hasProperty)
+ *hasProperty = true;
+ return getValue(pd, attrs);
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+
+// Section 8.12.5
+void Object::internalPut(String *name, const Value &value)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return putIndexed(idx, value);
+
+ name->makeIdentifier();
+
+ uint member = internalClass->find(name);
+ Property *pd = 0;
+ PropertyAttributes attrs;
+ if (member < UINT_MAX) {
+ pd = memberData + member;
+ attrs = internalClass->propertyData[member];
+ }
+
+ // clause 1
+ if (pd) {
+ if (attrs.isAccessor()) {
+ if (pd->setter())
+ goto cont;
+ goto reject;
+ } else if (!attrs.isWritable())
+ goto reject;
+ else if (isArrayObject() && name->isEqualTo(engine()->id_length)) {
+ bool ok;
+ uint l = value.asArrayLength(&ok);
+ if (!ok)
+ engine()->current->throwRangeError(value);
+ ok = setArrayLength(l);
+ if (!ok)
+ goto reject;
+ } else {
+ pd->value = value;
+ }
+ return;
+ } else if (!prototype) {
+ if (!extensible)
+ goto reject;
+ } else {
+ // clause 4
+ if ((pd = prototype->__getPropertyDescriptor__(name, &attrs))) {
+ if (attrs.isAccessor()) {
+ if (!pd->setter())
+ goto reject;
+ } else if (!extensible || !attrs.isWritable()) {
+ goto reject;
+ }
+ } else if (!extensible) {
+ goto reject;
+ }
+ }
+
+ cont:
+
+ // Clause 5
+ if (pd && attrs.isAccessor()) {
+ assert(pd->setter() != 0);
+
+ Value args[1];
+ args[0] = value;
+ pd->setter()->call(Value::fromObject(this), args, 1);
+ return;
+ }
+
+ {
+ Property *p = insertMember(name, Attr_Data);
+ p->value = value;
+ return;
+ }
+
+ reject:
+ if (engine()->current->strictMode) {
+ QString message = QStringLiteral("Cannot assign to read-only property \"");
+ message += name->toQString();
+ message += QLatin1Char('\"');
+ engine()->current->throwTypeError(message);
+ }
+}
+
+void Object::internalPutIndexed(uint index, const Value &value)
+{
+ Property *pd = 0;
+ PropertyAttributes attrs;
+
+ uint pidx = propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX) {
+ if (arrayAttributes && arrayAttributes[pidx].isGeneric()) {
+ pidx = UINT_MAX;
+ } else {
+ pd = arrayData + pidx;
+ attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ }
+ }
+
+ if (!pd && isStringObject()) {
+ pd = static_cast<StringObject *>(this)->getIndex(index);
+ if (pd)
+ // not writable
+ goto reject;
+ }
+
+ // clause 1
+ if (pd) {
+ if (attrs.isAccessor()) {
+ if (pd->setter())
+ goto cont;
+ goto reject;
+ } else if (!attrs.isWritable())
+ goto reject;
+ else
+ pd->value = value;
+ return;
+ } else if (!prototype) {
+ if (!extensible)
+ goto reject;
+ } else {
+ // clause 4
+ if ((pd = prototype->__getPropertyDescriptor__(index, &attrs))) {
+ if (attrs.isAccessor()) {
+ if (!pd->setter())
+ goto reject;
+ } else if (!extensible || !attrs.isWritable()) {
+ goto reject;
+ }
+ } else if (!extensible) {
+ goto reject;
+ }
+ }
+
+ cont:
+
+ // Clause 5
+ if (pd && attrs.isAccessor()) {
+ assert(pd->setter() != 0);
+
+ Value args[1];
+ args[0] = value;
+ pd->setter()->call(Value::fromObject(this), args, 1);
+ return;
+ }
+
+ arraySet(index, value);
+ return;
+
+ reject:
+ if (engine()->current->strictMode)
+ engine()->current->throwTypeError();
+}
+
+// Section 8.12.7
+bool Object::internalDeleteProperty(String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return deleteIndexedProperty(idx);
+
+ name->makeIdentifier();
+
+ uint memberIdx = internalClass->find(name);
+ if (memberIdx != UINT_MAX) {
+ if (internalClass->propertyData[memberIdx].isConfigurable()) {
+ internalClass->removeMember(this, name->identifier);
+ memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property));
+ return true;
+ }
+ if (engine()->current->strictMode)
+ engine()->current->throwTypeError();
+ return false;
+ }
+
+ return true;
+}
+
+bool Object::internalDeleteIndexedProperty(uint index)
+{
+ uint pidx = propertyIndexFromArrayIndex(index);
+ if (pidx == UINT_MAX)
+ return true;
+ if (arrayAttributes && arrayAttributes[pidx].isGeneric())
+ return true;
+
+ if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) {
+ arrayData[pidx].value = Value::undefinedValue();
+ if (!arrayAttributes)
+ ensureArrayAttributes();
+ arrayAttributes[pidx].clear();
+ if (sparseArray) {
+ arrayData[pidx].value.int_32 = arrayFreeList;
+ arrayFreeList = pidx;
+ }
+ return true;
+ }
+
+ if (engine()->current->strictMode)
+ engine()->current->throwTypeError();
+ return false;
+}
+
+// Section 8.12.9
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return __defineOwnProperty__(ctx, idx, p, attrs);
+
+ name->makeIdentifier();
+
+ Property *current;
+ PropertyAttributes *cattrs;
+
+ if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) {
+ assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length));
+ Property *lp = memberData + ArrayObject::LengthPropertyIndex;
+ cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex;
+ if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs))
+ return true;
+ if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
+ goto reject;
+ bool succeeded = true;
+ if (attrs.type() == PropertyAttributes::Data) {
+ bool ok;
+ uint l = p.value.asArrayLength(&ok);
+ if (!ok)
+ ctx->throwRangeError(p.value);
+ succeeded = setArrayLength(l);
+ }
+ if (attrs.hasWritable() && !attrs.isWritable())
+ cattrs->setWritable(false);
+ if (!succeeded)
+ goto reject;
+ return true;
+ }
+
+ // Clause 1
+ {
+ uint member = internalClass->find(name);
+ current = (member < UINT_MAX) ? memberData + member : 0;
+ cattrs = internalClass->propertyData.data() + member;
+ }
+
+ if (!current) {
+ // clause 3
+ if (!extensible)
+ goto reject;
+ // clause 4
+ Property *pd = insertMember(name, attrs);
+ *pd = p;
+ pd->fullyPopulated(&attrs);
+ return true;
+ }
+
+ return __defineOwnProperty__(ctx, current, name, p, attrs);
+reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+ return false;
+}
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs)
+{
+ Property *current = 0;
+
+ // 15.4.5.1, 4b
+ if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable())
+ goto reject;
+
+ if (isNonStrictArgumentsObject)
+ return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, p, attrs);
+
+ // Clause 1
+ {
+ uint pidx = propertyIndexFromArrayIndex(index);
+ if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric()))
+ current = arrayData + pidx;
+ if (!current && isStringObject())
+ current = static_cast<StringObject *>(this)->getIndex(index);
+ }
+
+ if (!current) {
+ // clause 3
+ if (!extensible)
+ goto reject;
+ // clause 4
+ Property *pd = arrayInsert(index, attrs);
+ *pd = p;
+ pd->fullyPopulated(&attrs);
+ return true;
+ }
+
+ return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs);
+reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+ return false;
+}
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs)
+{
+ // clause 5
+ if (attrs.isEmpty())
+ return true;
+
+ PropertyAttributes cattrs = Attr_Data;
+ if (member)
+ cattrs = internalClass->propertyData[current - memberData];
+ else if (arrayAttributes)
+ cattrs = arrayAttributes[current - arrayData];
+
+ // clause 6
+ if (p.isSubset(attrs, *current, cattrs))
+ return true;
+
+ // clause 7
+ if (!cattrs.isConfigurable()) {
+ if (attrs.isConfigurable())
+ goto reject;
+ if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable())
+ goto reject;
+ }
+
+ // clause 8
+ if (attrs.isGeneric())
+ goto accept;
+
+ // clause 9
+ if (cattrs.isData() != attrs.isData()) {
+ // 9a
+ if (!cattrs.isConfigurable())
+ goto reject;
+ if (cattrs.isData()) {
+ // 9b
+ cattrs.setType(PropertyAttributes::Accessor);
+ cattrs.clearWritable();
+ current->setGetter(0);
+ current->setSetter(0);
+ } else {
+ // 9c
+ cattrs.setType(PropertyAttributes::Data);
+ cattrs.setWritable(false);
+ current->value = Value::undefinedValue();
+ }
+ } else if (cattrs.isData() && attrs.isData()) { // clause 10
+ if (!cattrs.isConfigurable() && !cattrs.isWritable()) {
+ if (attrs.isWritable() || !current->value.sameValue(p.value))
+ goto reject;
+ }
+ } else { // clause 10
+ assert(cattrs.isAccessor() && attrs.isAccessor());
+ if (!cattrs.isConfigurable()) {
+ if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1)))
+ goto reject;
+ if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1)))
+ goto reject;
+ }
+ }
+
+ accept:
+
+ current->merge(cattrs, p, attrs);
+ if (member) {
+ internalClass = internalClass->changeMember(member, cattrs);
+ } else {
+ if (cattrs != Attr_Data)
+ ensureArrayAttributes();
+ if (arrayAttributes)
+ arrayAttributes[current - arrayData] = cattrs;
+ }
+ return true;
+ reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+ return false;
+}
+
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs)
+{
+ return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs);
+}
+
+
+void Object::copyArrayData(Object *other)
+{
+ arrayReserve(other->arrayDataLen);
+ arrayDataLen = other->arrayDataLen;
+ memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property));
+ arrayOffset = 0;
+ if (other->sparseArray) {
+ sparseArray = new SparseArray(*other->sparseArray);
+ arrayFreeList = other->arrayFreeList;
+ }
+ if (isArrayObject())
+ setArrayLengthUnchecked(other->arrayLength());
+}
+
+
+Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o)
+{
+ bool protoHasArray = false;
+ Object *p = o;
+ while ((p = p->prototype))
+ if (p->arrayDataLen)
+ protoHasArray = true;
+
+ if (protoHasArray || o->arrayAttributes) {
+ // lets be safe and slow
+ for (uint i = fromIndex; i < endIndex; ++i) {
+ bool exists;
+ Value value = o->getIndexed(i, &exists);
+ if (exists && __qmljs_strict_equal(value, v))
+ return Value::fromDouble(i);
+ }
+ } else if (sparseArray) {
+ for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) {
+ Value value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data);
+ if (__qmljs_strict_equal(value, v))
+ return Value::fromDouble(n->key());
+ }
+ } else {
+ if ((int) endIndex > arrayDataLen)
+ endIndex = arrayDataLen;
+ Property *pd = arrayData;
+ Property *end = pd + endIndex;
+ pd += fromIndex;
+ while (pd < end) {
+ if (!arrayAttributes || !arrayAttributes[pd - arrayData].isGeneric()) {
+ Value value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data);
+ if (__qmljs_strict_equal(value, v))
+ return Value::fromDouble(pd - arrayData);
+ }
+ ++pd;
+ }
+ }
+ return Value::fromInt32(-1);
+}
+
+void Object::arrayConcat(const ArrayObject *other)
+{
+ int newLen = arrayDataLen + other->arrayLength();
+ if (other->sparseArray)
+ initSparse();
+ // ### copy attributes as well!
+ if (sparseArray) {
+ if (other->sparseArray) {
+ for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode())
+ arraySet(arrayDataLen + it->key(), other->arrayData + it->value);
+ } else {
+ int oldSize = arrayDataLen;
+ arrayReserve(oldSize + other->arrayLength());
+ memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property));
+ if (arrayAttributes)
+ std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data));
+ for (uint i = 0; i < other->arrayLength(); ++i) {
+ SparseArrayNode *n = sparseArray->insert(arrayDataLen + i);
+ n->value = oldSize + i;
+ }
+ }
+ } else {
+ int oldSize = arrayLength();
+ arrayReserve(oldSize + other->arrayDataLen);
+ if (oldSize > arrayDataLen) {
+ ensureArrayAttributes();
+ std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes());
+ }
+ arrayDataLen = oldSize + other->arrayDataLen;
+ if (other->arrayAttributes) {
+ for (int i = 0; i < arrayDataLen; ++i) {
+ bool exists;
+ arrayData[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(i, &exists);
+ if (arrayAttributes)
+ arrayAttributes[oldSize + i] = Attr_Data;
+ if (!exists) {
+ ensureArrayAttributes();
+ arrayAttributes[oldSize + i].clear();
+ }
+ }
+ } else {
+ memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property));
+ if (arrayAttributes)
+ std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data));
+ }
+ }
+ setArrayLengthUnchecked(newLen);
+}
+
+void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len)
+{
+ if (!arrayDataLen)
+ return;
+
+ if (sparseArray) {
+ context->throwUnimplemented("Object::sort unimplemented for sparse arrays");
+ return;
+ }
+
+ if (len > arrayDataLen)
+ len = arrayDataLen;
+
+ // The spec says the sorting goes through a series of get,put and delete operations.
+ // this implies that the attributes don't get sorted around.
+ // behavior of accessor properties is implementation defined. We simply turn them all
+ // into data properties and then sort. This is in line with the sentence above.
+ if (arrayAttributes) {
+ for (uint i = 0; i < len; i++) {
+ if (arrayAttributes[i].isGeneric()) {
+ while (--len > i)
+ if (!arrayAttributes[len].isGeneric())
+ break;
+ arrayData[i].value = getValue(arrayData + len, arrayAttributes[len]);
+ arrayAttributes[i] = Attr_Data;
+ arrayAttributes[len].clear();
+ } else if (arrayAttributes[i].isAccessor()) {
+ arrayData[i].value = getValue(arrayData + i, arrayAttributes[i]);
+ arrayAttributes[i] = Attr_Data;
+ }
+ }
+ }
+
+ ArrayElementLessThan lessThan(context, thisObject, comparefn);
+
+ Property *begin = arrayData;
+ // We deliberately choose qSort over std::sort here, because with
+ // MSVC in debug builds, std::sort has an ASSERT() that verifies
+ // that the return values of lessThan are perfectly consistent
+ // and aborts otherwise. We do not want JavaScript to easily crash
+ // the entire application and therefore choose qSort, which doesn't
+ // have this property.
+ qSort(begin, begin + len, lessThan);
+}
+
+
+void Object::initSparse()
+{
+ if (!sparseArray) {
+ sparseArray = new SparseArray;
+ for (int i = 0; i < arrayDataLen; ++i) {
+ if (!arrayAttributes || !arrayAttributes[i].isGeneric()) {
+ SparseArrayNode *n = sparseArray->insert(i);
+ n->value = i + arrayOffset;
+ }
+ }
+
+ uint off = arrayOffset;
+ if (!arrayOffset) {
+ arrayFreeList = arrayDataLen;
+ } else {
+ arrayFreeList = 0;
+ arrayData -= off;
+ arrayAlloc += off;
+ int o = off;
+ for (int i = 0; i < o - 1; ++i) {
+ arrayData[i].value = Value::fromInt32(i + 1);
+ }
+ arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off);
+ }
+ for (int i = arrayDataLen + off; i < arrayAlloc; ++i) {
+ arrayData[i].value = Value::fromInt32(i + 1);
+ }
+ }
+}
+
+void Object::arrayReserve(uint n)
+{
+ if (n < 8)
+ n = 8;
+ if (n >= arrayAlloc) {
+ uint off;
+ if (sparseArray) {
+ assert(arrayFreeList == arrayAlloc);
+ // ### FIXME
+ arrayDataLen = arrayAlloc;
+ off = 0;
+ } else {
+ off = arrayOffset;
+ }
+ arrayAlloc = qMax(n, 2*arrayAlloc);
+ Property *newArrayData = new Property[arrayAlloc];
+ if (arrayData) {
+ memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen);
+ delete [] (arrayData - off);
+ }
+ arrayData = newArrayData;
+ if (sparseArray) {
+ for (uint i = arrayFreeList; i < arrayAlloc; ++i) {
+ arrayData[i].value = Value::emptyValue();
+ arrayData[i].value = Value::fromInt32(i + 1);
+ }
+ } else {
+ arrayOffset = 0;
+ }
+
+ if (arrayAttributes) {
+ PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc];
+ memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen);
+ delete [] (arrayAttributes - off);
+
+ arrayAttributes = newAttrs;
+ if (sparseArray) {
+ for (uint i = arrayFreeList; i < arrayAlloc; ++i)
+ arrayAttributes[i] = Attr_Invalid;
+ }
+ }
+ }
+}
+
+void Object::ensureArrayAttributes()
+{
+ if (arrayAttributes)
+ return;
+
+ arrayAttributes = new PropertyAttributes[arrayAlloc];
+ for (uint i = 0; i < arrayDataLen; ++i)
+ arrayAttributes[i] = Attr_Data;
+ for (uint i = arrayDataLen; i < arrayAlloc; ++i)
+ arrayAttributes[i] = Attr_Invalid;
+}
+
+
+bool Object::setArrayLength(uint newLen) {
+ assert(isArrayObject());
+ const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex;
+ if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable())
+ return false;
+ uint oldLen = arrayLength();
+ bool ok = true;
+ if (newLen < oldLen) {
+ if (sparseArray) {
+ SparseArrayNode *begin = sparseArray->lowerBound(newLen);
+ if (begin != sparseArray->end()) {
+ SparseArrayNode *it = sparseArray->end()->previousNode();
+ while (1) {
+ Property &pd = arrayData[it->value];
+ if (arrayAttributes) {
+ if (!arrayAttributes[it->value].isConfigurable()) {
+ ok = false;
+ newLen = it->key() + 1;
+ break;
+ } else {
+ arrayAttributes[it->value].clear();
+ }
+ }
+ pd.value.tag = Value::_Empty_Type;
+ pd.value.int_32 = arrayFreeList;
+ arrayFreeList = it->value;
+ bool brk = (it == begin);
+ SparseArrayNode *prev = it->previousNode();
+ sparseArray->erase(it);
+ if (brk)
+ break;
+ it = prev;
+ }
+ }
+ } else {
+ Property *it = arrayData + arrayDataLen;
+ const Property *begin = arrayData + newLen;
+ while (--it >= begin) {
+ if (arrayAttributes) {
+ if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) {
+ ok = false;
+ newLen = it - arrayData + 1;
+ break;
+ } else {
+ arrayAttributes[it - arrayData].clear();
+ }
+ it->value = Value::emptyValue();
+ }
+ }
+ arrayDataLen = newLen;
+ }
+ } else {
+ if (newLen >= 0x100000)
+ initSparse();
+ }
+ setArrayLengthUnchecked(newLen);
+ return ok;
+}
+
+void Object::markArrayObjects() const
+{
+ for (uint i = 0; i < arrayDataLen; ++i) {
+ const Property &pd = arrayData[i];
+ if (!arrayAttributes || arrayAttributes[i].isData()) {
+ if (Managed *m = pd.value.asManaged())
+ m->mark();
+ } else if (arrayAttributes[i].isAccessor()) {
+ if (pd.getter())
+ pd.getter()->mark();
+ if (pd.setter())
+ pd.setter()->mark();
+ }
+ }
+}
+
+ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list)
+ : Object(engine)
+{
+ init(engine);
+
+ // Converts a QStringList to JS.
+ // The result is a new Array object with length equal to the length
+ // of the QStringList, and the elements being the QStringList's
+ // elements converted to JS Strings.
+ int len = list.count();
+ arrayReserve(len);
+ for (int ii = 0; ii < len; ++ii)
+ arrayData[ii].value = Value::fromString(engine->newString(list.at(ii)));
+ setArrayLengthUnchecked(len);
+}
+
+void ArrayObject::init(ExecutionEngine *engine)
+{
+ type = Type_ArrayObject;
+ internalClass = engine->arrayClass;
+
+ memberData = new Property[4];
+ memberData[LengthPropertyIndex].value = Value::fromInt32(0);
+}
+
+QStringList ArrayObject::toQStringList() const
+{
+ QStringList result;
+
+ QV4::ExecutionEngine *engine = internalClass->engine;
+
+ uint32_t length = arrayLength();
+ for (uint32_t i = 0; i < length; ++i)
+ result.append(const_cast<ArrayObject *>(this)->getIndexed(i).toString(engine->current)->toQString());
+ return result;
+}
+
+
+DEFINE_MANAGED_VTABLE(ForEachIteratorObject);
+
+void ForEachIteratorObject::markObjects(Managed *that)
+{
+ ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that);
+ Object::markObjects(that);
+ if (o->it.object)
+ o->it.object->mark();
+}
diff --git a/src/qml/qml/v4/qv4object_p.h b/src/qml/qml/v4/qv4object_p.h
new file mode 100644
index 0000000000..a6f1c04abd
--- /dev/null
+++ b/src/qml/qml/v4/qv4object_p.h
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_OBJECTS_H
+#define QMLJS_OBJECTS_H
+
+#include "qv4global_p.h"
+#include "qv4runtime_p.h"
+#include "qv4engine_p.h"
+#include "qv4context_p.h"
+#include "qv4sparsearray_p.h"
+#include "qv4string_p.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed_p.h"
+#include "qv4property_p.h"
+#include "qv4internalclass_p.h"
+#include "qv4objectiterator_p.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+struct Lookup;
+struct Object;
+struct ObjectIterator;
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct ArrayObject;
+struct DateObject;
+struct FunctionObject;
+struct RegExpObject;
+struct ErrorObject;
+struct ArgumentsObject;
+struct ExecutionContext;
+struct CallContext;
+struct ExecutionEngine;
+class MemoryManager;
+
+struct ObjectPrototype;
+struct StringPrototype;
+struct NumberPrototype;
+struct BooleanPrototype;
+struct ArrayPrototype;
+struct FunctionPrototype;
+struct DatePrototype;
+struct RegExpPrototype;
+struct ErrorPrototype;
+struct EvalErrorPrototype;
+struct RangeErrorPrototype;
+struct ReferenceErrorPrototype;
+struct SyntaxErrorPrototype;
+struct TypeErrorPrototype;
+struct URIErrorPrototype;
+
+typedef Value (*PropertyEnumeratorFunction)(Object *object);
+typedef PropertyAttributes (*PropertyQueryFunction)(const Object *object, String *name);
+
+struct Q_QML_EXPORT Object: Managed {
+ Object *prototype;
+ uint memberDataAlloc;
+ Property *memberData;
+
+ union {
+ uint arrayFreeList;
+ uint arrayOffset;
+ };
+ uint arrayDataLen;
+ uint arrayAlloc;
+ PropertyAttributes *arrayAttributes;
+ Property *arrayData;
+ SparseArray *sparseArray;
+
+ enum {
+ InlinePropertySize = 4
+ };
+ Property inlineProperties[InlinePropertySize];
+
+ Object(ExecutionEngine *engine);
+ Object(ExecutionContext *context);
+ Object(ExecutionEngine *engine, InternalClass *internalClass);
+ ~Object();
+
+ Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0);
+ Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0);
+
+ Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const;
+ Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const;
+
+ bool __hasProperty__(String *name) const;
+ bool __hasProperty__(uint index) const {
+ return __getPropertyDescriptor__(index);
+ }
+
+ bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs);
+
+ //
+ // helpers
+ //
+ void put(ExecutionContext *ctx, const QString &name, const Value &value);
+
+ static Value getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs);
+ Value getValue(const Property *p, PropertyAttributes attrs) const {
+ return getValue(Value::fromObject(const_cast<Object *>(this)), p, attrs);
+ }
+
+ void putValue(Property *pd, PropertyAttributes attrs, const Value &value);
+
+ void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs);
+ void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs);
+
+ /* The spec default: Writable: true, Enumerable: false, Configurable: true */
+ void defineDefaultProperty(String *name, Value value);
+ void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value);
+ void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value);
+ void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0);
+ void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int count = 0);
+ void defineAccessorProperty(ExecutionEngine *engine, const QString &name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *));
+ void defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *));
+ /* Fixed: Writable: false, Enumerable: false, Configurable: false */
+ void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value);
+ void defineReadonlyProperty(String *name, Value value);
+
+ Property *insertMember(String *s, PropertyAttributes attributes);
+
+ inline ExecutionEngine *engine() const { return internalClass->engine; }
+
+ // Array handling
+
+ uint allocArrayValue() {
+ uint idx = arrayFreeList;
+ if (arrayAlloc <= arrayFreeList)
+ arrayReserve(arrayAlloc + 1);
+ arrayFreeList = arrayData[arrayFreeList].value.uint_32;
+ if (arrayAttributes)
+ arrayAttributes[idx].setType(PropertyAttributes::Data);
+ return idx;
+ }
+
+ uint allocArrayValue(Value v) {
+ uint idx = allocArrayValue();
+ Property *pd = &arrayData[idx];
+ pd->value = v;
+ return idx;
+ }
+ void freeArrayValue(int idx) {
+ Property &pd = arrayData[idx];
+ pd.value.tag = Value::_Empty_Type;
+ pd.value.int_32 = arrayFreeList;
+ arrayFreeList = idx;
+ if (arrayAttributes)
+ arrayAttributes[idx].clear();
+ }
+
+ void getArrayHeadRoom() {
+ assert(!sparseArray && !arrayOffset);
+ arrayOffset = qMax(arrayDataLen >> 2, (uint)16);
+ Property *newArray = new Property[arrayOffset + arrayAlloc];
+ memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property));
+ delete [] arrayData;
+ arrayData = newArray + arrayOffset;
+ if (arrayAttributes) {
+ PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc];
+ memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes));
+ delete [] arrayAttributes;
+ arrayAttributes = newAttrs + arrayOffset;
+ }
+ }
+
+public:
+ void copyArrayData(Object *other);
+ void initSparse();
+
+ uint arrayLength() const;
+ bool setArrayLength(uint newLen);
+
+ void setArrayLengthUnchecked(uint l);
+
+ Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) {
+
+ Property *pd;
+ if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) {
+ if (index >= arrayAlloc)
+ arrayReserve(index + 1);
+ if (index >= arrayDataLen) {
+ ensureArrayAttributes();
+ for (uint i = arrayDataLen; i < index; ++i)
+ arrayAttributes[i].clear();
+ arrayDataLen = index + 1;
+ }
+ pd = arrayData + index;
+ } else {
+ initSparse();
+ SparseArrayNode *n = sparseArray->insert(index);
+ if (n->value == UINT_MAX)
+ n->value = allocArrayValue();
+ pd = arrayData + n->value;
+ }
+ if (index >= arrayLength())
+ setArrayLengthUnchecked(index + 1);
+ if (arrayAttributes || attributes != Attr_Data) {
+ if (!arrayAttributes)
+ ensureArrayAttributes();
+ attributes.resolve();
+ arrayAttributes[pd - arrayData] = attributes;
+ }
+ return pd;
+ }
+
+ void arraySet(uint index, const Property *pd) {
+ *arrayInsert(index) = *pd;
+ }
+
+ void arraySet(uint index, Value value) {
+ Property *pd = arrayInsert(index);
+ pd->value = value;
+ }
+
+ uint propertyIndexFromArrayIndex(uint index) const
+ {
+ if (!sparseArray) {
+ if (index >= arrayDataLen)
+ return UINT_MAX;
+ return index;
+ } else {
+ SparseArrayNode *n = sparseArray->findNode(index);
+ if (!n)
+ return UINT_MAX;
+ return n->value;
+ }
+ }
+
+ Property *arrayAt(uint index) const {
+ uint pidx = propertyIndexFromArrayIndex(index);
+ if (pidx == UINT_MAX)
+ return 0;
+ return arrayData + pidx;
+ }
+
+ Property *nonSparseArrayAt(uint index) const {
+ if (sparseArray)
+ return 0;
+ if (index >= arrayDataLen)
+ return 0;
+ return arrayData + index;
+ }
+
+ void markArrayObjects() const;
+
+ void push_back(Value v) {
+ uint idx = arrayLength();
+ if (!sparseArray) {
+ if (idx >= arrayAlloc)
+ arrayReserve(idx + 1);
+ arrayData[idx].value = v;
+ arrayDataLen = idx + 1;
+ } else {
+ uint idx = allocArrayValue(v);
+ sparseArray->push_back(idx, arrayLength());
+ }
+ setArrayLengthUnchecked(idx + 1);
+ }
+
+ SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; }
+ SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; }
+
+ void arrayConcat(const ArrayObject *other);
+ void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen);
+ Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o);
+
+ void arrayReserve(uint n);
+ void ensureArrayAttributes();
+
+ inline Value get(String *name, bool *hasProperty = 0)
+ { return vtbl->get(this, name, hasProperty); }
+ inline Value getIndexed(uint idx, bool *hasProperty = 0)
+ { return vtbl->getIndexed(this, idx, hasProperty); }
+ inline void put(String *name, const Value &v)
+ { vtbl->put(this, name, v); }
+ inline void putIndexed(uint idx, const Value &v)
+ { vtbl->putIndexed(this, idx, v); }
+ using Managed::get;
+ using Managed::getIndexed;
+ using Managed::put;
+ using Managed::putIndexed;
+ using Managed::query;
+ using Managed::queryIndexed;
+ using Managed::deleteProperty;
+ using Managed::deleteIndexedProperty;
+ using Managed::getLookup;
+ using Managed::setLookup;
+ using Managed::advanceIterator;
+protected:
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+ static Value get(Managed *m, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, uint index, bool *hasProperty);
+ static void put(Managed *m, String *name, const Value &value);
+ static void putIndexed(Managed *m, uint index, const Value &value);
+ static PropertyAttributes query(const Managed *m, String *name);
+ static PropertyAttributes queryIndexed(const Managed *m, uint index);
+ static bool deleteProperty(Managed *m, String *name);
+ static bool deleteIndexedProperty(Managed *m, uint index);
+ static void getLookup(Managed *m, Lookup *l, Value *result);
+ static void setLookup(Managed *m, Lookup *l, const Value &v);
+ static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes);
+
+
+private:
+ Value internalGet(String *name, bool *hasProperty);
+ Value internalGetIndexed(uint index, bool *hasProperty);
+ void internalPut(String *name, const Value &value);
+ void internalPutIndexed(uint index, const Value &value);
+ bool internalDeleteProperty(String *name);
+ bool internalDeleteIndexedProperty(uint index);
+
+ friend struct ObjectIterator;
+ friend struct ObjectPrototype;
+};
+
+struct ForEachIteratorObject: Object {
+ Q_MANAGED
+ ObjectIterator it;
+ ForEachIteratorObject(ExecutionContext *ctx, Object *o)
+ : Object(ctx->engine), it(o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) {
+ vtbl = &static_vtbl;
+ type = Type_ForeachIteratorObject;
+ }
+
+ Value nextPropertyName() { return it.nextPropertyNameAsString(); }
+
+protected:
+ static void markObjects(Managed *that);
+};
+
+struct BooleanObject: Object {
+ Value value;
+ BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; }
+};
+
+struct NumberObject: Object {
+ Value value;
+ NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; }
+};
+
+struct ArrayObject: Object {
+ enum {
+ LengthPropertyIndex = 0
+ };
+
+ ArrayObject(ExecutionEngine *engine) : Object(engine) { init(engine); }
+ ArrayObject(ExecutionEngine *engine, const QStringList &list);
+ void init(ExecutionEngine *engine);
+
+ QStringList toQStringList() const;
+};
+
+inline uint Object::arrayLength() const
+{
+ if (isArrayObject()) {
+ // length is always the first property of an array
+ Value v = memberData[ArrayObject::LengthPropertyIndex].value;
+ if (v.isInteger())
+ return v.integerValue();
+ return Value::toUInt32(v.doubleValue());
+ }
+ return 0;
+}
+
+inline void Object::setArrayLengthUnchecked(uint l)
+{
+ if (isArrayObject()) {
+ // length is always the first property of an array
+ Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex];
+ lengthProperty.value = Value::fromUInt32(l);
+ }
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4/qv4objectiterator.cpp b/src/qml/qml/v4/qv4objectiterator.cpp
new file mode 100644
index 0000000000..a89bfdb797
--- /dev/null
+++ b/src/qml/qml/v4/qv4objectiterator.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4objectiterator_p.h"
+#include "qv4object_p.h"
+#include "qv4stringobject_p.h"
+#include "qv4identifier_p.h"
+
+using namespace QV4;
+
+ObjectIterator::ObjectIterator(Object *o, uint flags)
+ : object(o)
+ , current(o)
+ , arrayNode(0)
+ , arrayIndex(0)
+ , memberIndex(0)
+ , flags(flags)
+{
+ tmpDynamicProperty.value = Value::undefinedValue();
+}
+
+Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs)
+{
+ Property *p = 0;
+ *name = 0;
+ *index = UINT_MAX;
+ while (1) {
+ if (!current)
+ break;
+
+ while (p = current->advanceIterator(this, name, index, attrs)) {
+ // check the property is not already defined earlier in the proto chain
+ if (current != object) {
+ Property *pp;
+ if (*name) {
+ pp = object->__getPropertyDescriptor__(*name);
+ } else {
+ assert (*index != UINT_MAX);
+ pp = object->__getPropertyDescriptor__(*index);
+ }
+ if (pp != p)
+ continue;
+ }
+ return p;
+ }
+
+ if (flags & WithProtoChain)
+ current = current->prototype;
+ else
+ current = 0;
+
+ arrayIndex = 0;
+ memberIndex = 0;
+ }
+ return 0;
+}
+
+Value ObjectIterator::nextPropertyName(Value *value)
+{
+ PropertyAttributes attrs;
+ uint index;
+ String *name;
+ Property *p = next(&name, &index, &attrs);
+ if (!p)
+ return Value::nullValue();
+
+ if (value)
+ *value = object->getValue(p, attrs);
+
+ if (name)
+ return Value::fromString(name);
+ assert(index < UINT_MAX);
+ return Value::fromDouble(index);
+}
+
+Value ObjectIterator::nextPropertyNameAsString(Value *value)
+{
+ PropertyAttributes attrs;
+ uint index;
+ String *name;
+ Property *p = next(&name, &index, &attrs);
+ if (!p)
+ return Value::nullValue();
+
+ if (value)
+ *value = object->getValue(p, attrs);
+
+ if (name)
+ return Value::fromString(name);
+ assert(index < UINT_MAX);
+ return Value::fromString(object->engine()->newString(QString::number(index)));
+}
diff --git a/src/qml/qml/v4/qv4objectiterator_p.h b/src/qml/qml/v4/qv4objectiterator_p.h
new file mode 100644
index 0000000000..95439397f5
--- /dev/null
+++ b/src/qml/qml/v4/qv4objectiterator_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4OBJECTITERATOR_H
+#define QV4OBJECTITERATOR_H
+
+#include "qv4global_p.h"
+#include "qv4property_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct SparseArrayNode;
+struct Object;
+struct ArrayObject;
+struct PropertyAttributes;
+struct ExecutionContext;
+struct Property;
+struct String;
+struct InternalClass;
+
+struct Q_QML_EXPORT ObjectIterator
+{
+ enum Flags {
+ NoFlags = 0,
+ EnumerableOnly = 0x1,
+ WithProtoChain = 0x2,
+ };
+
+ Object *object;
+ Object *current;
+ SparseArrayNode *arrayNode;
+ uint arrayIndex;
+ uint memberIndex;
+ uint flags;
+
+ Property tmpDynamicProperty;
+
+ ObjectIterator(Object *o, uint flags);
+ Property *next(String **name, uint *index, PropertyAttributes *attributes = 0);
+ Value nextPropertyName(Value *value = 0);
+ Value nextPropertyNameAsString(Value *value = 0);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4objectproto.cpp b/src/qml/qml/v4/qv4objectproto.cpp
new file mode 100644
index 0000000000..462c9ca81e
--- /dev/null
+++ b/src/qml/qml/v4/qv4objectproto.cpp
@@ -0,0 +1,624 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4objectproto_p.h"
+#include "qv4mm_p.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDateTime>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+#include <cassert>
+
+#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 <qv4isel_masm_p.h>
+
+#ifndef Q_OS_WIN
+# include <time.h>
+# ifndef Q_OS_VXWORKS
+# include <sys/time.h>
+# else
+# include "qplatformdefs.h"
+# endif
+#else
+# include <windows.h>
+#endif
+
+using namespace QV4;
+
+
+DEFINE_MANAGED_VTABLE(ObjectCtor);
+
+ObjectCtor::ObjectCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Object")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value ObjectCtor::construct(Managed *that, Value *args, int argc)
+{
+ ObjectCtor *ctor = static_cast<ObjectCtor *>(that);
+ ExecutionEngine *v4 = that->engine();
+ if (!argc || args[0].isUndefined() || args[0].isNull()) {
+ Object *obj = v4->newObject();
+ Value proto = ctor->get(v4->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+ return Value::fromObject(obj);
+ }
+ return __qmljs_to_object(v4->current, args[0]);
+}
+
+Value ObjectCtor::call(Managed *m, const Value &/*thisObject*/, Value *args, int argc)
+{
+ if (!argc || args[0].isUndefined() || args[0].isNull())
+ return Value::fromObject(m->engine()->newObject());
+ return __qmljs_to_object(m->engine()->current, args[0]);
+}
+
+void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1);
+ ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1);
+
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0);
+ defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1);
+ defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1);
+ defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1);
+ defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 2);
+ defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 2);
+
+ ExecutionEngine *v4 = ctx->engine;
+ Property *p = insertMember(v4->id___proto__, Attr_Accessor|Attr_NotEnumerable);
+ p->setGetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_get_proto));
+ p->setSetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_set_proto));
+}
+
+Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx)
+{
+ Value o = ctx->argument(0);
+ if (! o.isObject())
+ ctx->throwTypeError();
+
+ Object *p = o.objectValue()->prototype;
+ return p ? Value::fromObject(p) : Value::nullValue();
+}
+
+Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ String *name = ctx->argument(1).toString(ctx);
+ PropertyAttributes attrs;
+ Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs);
+ return fromPropertyDescriptor(ctx, desc, attrs);
+}
+
+Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context)
+{
+ Object *O = context->argumentCount ? context->arguments[0].asObject() : 0;
+ if (!O)
+ context->throwTypeError();
+
+ ArrayObject *array = getOwnPropertyNames(context->engine, context->arguments[0]);
+ return Value::fromObject(array);
+}
+
+Value ObjectPrototype::method_create(SimpleCallContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject() && !O.isNull())
+ ctx->throwTypeError();
+
+ Object *newObject = ctx->engine->newObject();
+ newObject->prototype = O.asObject();
+
+ Value objValue = Value::fromObject(newObject);
+ if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) {
+ ctx->arguments[0] = objValue;
+ method_defineProperties(ctx);
+ }
+
+ return objValue;
+}
+
+Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ String *name = ctx->argument(1).toString(ctx);
+
+ Value attributes = ctx->argument(2);
+ Property pd;
+ PropertyAttributes attrs;
+ toPropertyDescriptor(ctx, attributes, &pd, &attrs);
+
+ if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs))
+ ctx->throwTypeError();
+
+ return O;
+}
+
+Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(1).toObject(ctx);
+
+ ObjectIterator it(o, ObjectIterator::EnumerableOnly);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyAttributes attrs;
+ Property *pd = it.next(&name, &index, &attrs);
+ if (!pd)
+ break;
+ Property n;
+ PropertyAttributes nattrs;
+ toPropertyDescriptor(ctx, o->getValue(pd, attrs), &n, &nattrs);
+ bool ok;
+ if (name)
+ ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs);
+ else
+ ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs);
+ if (!ok)
+ ctx->throwTypeError();
+ }
+
+ return O;
+}
+
+Value ObjectPrototype::method_seal(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+
+ o->internalClass = o->internalClass->sealed();
+
+ o->ensureArrayAttributes();
+ for (uint i = 0; i < o->arrayDataLen; ++i) {
+ if (!o->arrayAttributes[i].isGeneric())
+ o->arrayAttributes[i].setConfigurable(false);
+ }
+
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_freeze(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+
+ o->internalClass = o->internalClass->frozen();
+
+ o->ensureArrayAttributes();
+ for (uint i = 0; i < o->arrayDataLen; ++i) {
+ if (!o->arrayAttributes[i].isGeneric())
+ o->arrayAttributes[i].setConfigurable(false);
+ if (o->arrayAttributes[i].isData())
+ o->arrayAttributes[i].setWritable(false);
+ }
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ if (o->extensible)
+ return Value::fromBoolean(false);
+
+ if (o->internalClass != o->internalClass->sealed())
+ return Value::fromBoolean(false);
+
+ if (!o->arrayDataLen)
+ return Value::fromBoolean(true);
+
+ if (!o->arrayAttributes)
+ return Value::fromBoolean(false);
+
+ for (uint i = 0; i < o->arrayDataLen; ++i) {
+ if (!o->arrayAttributes[i].isGeneric())
+ if (o->arrayAttributes[i].isConfigurable())
+ return Value::fromBoolean(false);
+ }
+
+ return Value::fromBoolean(true);
+}
+
+Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ if (o->extensible)
+ return Value::fromBoolean(false);
+
+ if (o->internalClass != o->internalClass->frozen())
+ return Value::fromBoolean(false);
+
+ if (!o->arrayDataLen)
+ return Value::fromBoolean(true);
+
+ if (!o->arrayAttributes)
+ return Value::fromBoolean(false);
+
+ for (uint i = 0; i < o->arrayDataLen; ++i) {
+ if (!o->arrayAttributes[i].isGeneric())
+ if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable())
+ return Value::fromBoolean(false);
+ }
+
+ return Value::fromBoolean(true);
+}
+
+Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+ return Value::fromBoolean(o->extensible);
+}
+
+Value ObjectPrototype::method_keys(SimpleCallContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(0).objectValue();
+
+ ArrayObject *a = ctx->engine->newArrayObject();
+
+ ObjectIterator it(o, ObjectIterator::EnumerableOnly);
+ while (1) {
+ Value name = it.nextPropertyNameAsString();
+ if (name.isNull())
+ break;
+ a->push_back(name);
+ }
+
+ return Value::fromObject(a);
+}
+
+Value ObjectPrototype::method_toString(SimpleCallContext *ctx)
+{
+ if (ctx->thisObject.isUndefined()) {
+ return Value::fromString(ctx, QStringLiteral("[object Undefined]"));
+ } else if (ctx->thisObject.isNull()) {
+ return Value::fromString(ctx, QStringLiteral("[object Null]"));
+ } else {
+ Value obj = __qmljs_to_object(ctx, ctx->thisObject);
+ QString className = obj.objectValue()->className();
+ return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className));
+ }
+}
+
+Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.toObject(ctx);
+ Value ts = o->get(ctx->engine->newString(QStringLiteral("toString")));
+ FunctionObject *f = ts.asFunctionObject();
+ if (!f)
+ ctx->throwTypeError();
+ return f->call(Value::fromObject(o), 0, 0);
+}
+
+Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx)
+{
+ return Value::fromObject(ctx->thisObject.toObject(ctx));
+}
+
+Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx)
+{
+ String *P = ctx->argument(0).toString(ctx);
+ Object *O = ctx->thisObject.toObject(ctx);
+ bool r = O->__getOwnProperty__(P) != 0;
+ return Value::fromBoolean(r);
+}
+
+Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx)
+{
+ Value V = ctx->argument(0);
+ if (! V.isObject())
+ return Value::fromBoolean(false);
+
+ Object *O = ctx->thisObject.toObject(ctx);
+ Object *proto = V.objectValue()->prototype;
+ while (proto) {
+ if (O == proto)
+ return Value::fromBoolean(true);
+ proto = proto->prototype;
+ }
+ return Value::fromBoolean(false);
+}
+
+Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx)
+{
+ String *p = ctx->argument(0).toString(ctx);
+
+ Object *o = ctx->thisObject.toObject(ctx);
+ PropertyAttributes attrs;
+ o->__getOwnProperty__(p, &attrs);
+ return Value::fromBoolean(attrs.isEnumerable());
+}
+
+Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx)
+{
+ if (ctx->argumentCount < 2)
+ ctx->throwTypeError();
+ String *prop = ctx->argument(0).toString(ctx);
+
+ FunctionObject *f = ctx->argument(1).asFunctionObject();
+ if (!f)
+ ctx->throwTypeError();
+
+ Object *o = ctx->thisObject.asObject();
+ if (!o) {
+ if (!ctx->thisObject.isUndefined())
+ return Value::undefinedValue();
+ o = ctx->engine->globalObject;
+ }
+
+ Property pd = Property::fromAccessor(f, 0);
+ o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor);
+ return Value::undefinedValue();
+}
+
+Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx)
+{
+ if (ctx->argumentCount < 2)
+ ctx->throwTypeError();
+ String *prop = ctx->argument(0).toString(ctx);
+
+ FunctionObject *f = ctx->argument(1).asFunctionObject();
+ if (!f)
+ ctx->throwTypeError();
+
+ Object *o = ctx->thisObject.asObject();
+ if (!o) {
+ if (!ctx->thisObject.isUndefined())
+ return Value::undefinedValue();
+ o = ctx->engine->globalObject;
+ }
+
+ Property pd = Property::fromAccessor(0, f);
+ o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor);
+ return Value::undefinedValue();
+}
+
+Value ObjectPrototype::method_get_proto(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ return Value::fromObject(o->prototype);
+}
+
+Value ObjectPrototype::method_set_proto(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ Value proto = ctx->argument(0);
+ bool ok = false;
+ if (proto.isNull()) {
+ o->prototype = 0;
+ ok = true;
+ } else if (Object *p = proto.asObject()) {
+ if (o->prototype == p) {
+ ok = true;
+ } else if (o->extensible) {
+ Object *pp = p;
+ while (pp) {
+ if (pp == o)
+ break;
+ pp = pp->prototype;
+ }
+ if (!pp) {
+ ok = true;
+ o->prototype = p;
+ }
+ }
+ }
+ if (!ok)
+ ctx->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
+ return Value::undefinedValue();
+}
+
+void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs)
+{
+ if (!v.isObject())
+ ctx->throwTypeError();
+
+ Object *o = v.objectValue();
+
+ attrs->clear();
+ desc->setGetter(0);
+ desc->setSetter(0);
+
+ if (o->__hasProperty__(ctx->engine->id_enumerable))
+ attrs->setEnumerable(o->get(ctx->engine->id_enumerable).toBoolean());
+
+ if (o->__hasProperty__(ctx->engine->id_configurable))
+ attrs->setConfigurable(o->get(ctx->engine->id_configurable).toBoolean());
+
+ if (o->__hasProperty__(ctx->engine->id_get)) {
+ Value get = o->get(ctx->engine->id_get);
+ FunctionObject *f = get.asFunctionObject();
+ if (f) {
+ desc->setGetter(f);
+ } else if (get.isUndefined()) {
+ desc->setGetter((FunctionObject *)0x1);
+ } else {
+ ctx->throwTypeError();
+ }
+ attrs->setType(PropertyAttributes::Accessor);
+ }
+
+ if (o->__hasProperty__(ctx->engine->id_set)) {
+ Value set = o->get(ctx->engine->id_set);
+ FunctionObject *f = set.asFunctionObject();
+ if (f) {
+ desc->setSetter(f);
+ } else if (set.isUndefined()) {
+ desc->setSetter((FunctionObject *)0x1);
+ } else {
+ ctx->throwTypeError();
+ }
+ attrs->setType(PropertyAttributes::Accessor);
+ }
+
+ if (o->__hasProperty__(ctx->engine->id_writable)) {
+ if (attrs->isAccessor())
+ ctx->throwTypeError();
+ attrs->setWritable(o->get(ctx->engine->id_writable).toBoolean());
+ // writable forces it to be a data descriptor
+ desc->value = Value::undefinedValue();
+ }
+
+ if (o->__hasProperty__(ctx->engine->id_value)) {
+ if (attrs->isAccessor())
+ ctx->throwTypeError();
+ desc->value = o->get(ctx->engine->id_value);
+ attrs->setType(PropertyAttributes::Data);
+ }
+
+ if (attrs->isGeneric())
+ desc->value = Value::emptyValue();
+}
+
+
+Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs)
+{
+ if (!desc)
+ return Value::undefinedValue();
+
+ ExecutionEngine *engine = ctx->engine;
+// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name.
+ Object *o = engine->newObject();
+
+ Property pd;
+ if (attrs.isData()) {
+ pd.value = desc->value;
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data);
+ pd.value = Value::fromBoolean(attrs.isWritable());
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data);
+ } else {
+ pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue();
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data);
+ pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue();
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data);
+ }
+ pd.value = Value::fromBoolean(attrs.isEnumerable());
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data);
+ pd.value = Value::fromBoolean(attrs.isConfigurable());
+ o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data);
+
+ return Value::fromObject(o);
+}
+
+
+ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o)
+{
+ ArrayObject *array = v4->newArrayObject();
+ Object *O = o.asObject();
+ if (!O)
+ return array;
+
+ ObjectIterator it(O, ObjectIterator::NoFlags);
+ while (1) {
+ Value name = it.nextPropertyNameAsString();
+ if (name.isNull())
+ break;
+ array->push_back(name);
+ }
+ return array;
+}
diff --git a/src/qml/qml/v4/qv4objectproto_p.h b/src/qml/qml/v4/qv4objectproto_p.h
new file mode 100644
index 0000000000..ca2e77ca42
--- /dev/null
+++ b/src/qml/qml/v4/qv4objectproto_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ECMAOBJECTS_P_H
+#define QV4ECMAOBJECTS_P_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ObjectCtor: FunctionObject
+{
+ ObjectCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ObjectPrototype: Object
+{
+ ObjectPrototype(ExecutionEngine *engine) : Object(engine) {}
+
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_getPrototypeOf(SimpleCallContext *ctx);
+ static Value method_getOwnPropertyDescriptor(SimpleCallContext *ctx);
+ static Value method_getOwnPropertyNames(SimpleCallContext *context);
+ static Value method_create(SimpleCallContext *ctx);
+ static Value method_defineProperty(SimpleCallContext *ctx);
+ static Value method_defineProperties(SimpleCallContext *ctx);
+ static Value method_seal(SimpleCallContext *ctx);
+ static Value method_freeze(SimpleCallContext *ctx);
+ static Value method_preventExtensions(SimpleCallContext *ctx);
+ static Value method_isSealed(SimpleCallContext *ctx);
+ static Value method_isFrozen(SimpleCallContext *ctx);
+ static Value method_isExtensible(SimpleCallContext *ctx);
+ static Value method_keys(SimpleCallContext *ctx);
+
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_toLocaleString(SimpleCallContext *ctx);
+ static Value method_valueOf(SimpleCallContext *ctx);
+ static Value method_hasOwnProperty(SimpleCallContext *ctx);
+ static Value method_isPrototypeOf(SimpleCallContext *ctx);
+ static Value method_propertyIsEnumerable(SimpleCallContext *ctx);
+
+ static Value method_defineGetter(SimpleCallContext *ctx);
+ static Value method_defineSetter(SimpleCallContext *ctx);
+
+ static Value method_get_proto(SimpleCallContext *ctx);
+ static Value method_set_proto(SimpleCallContext *ctx);
+
+ static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs);
+ static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs);
+
+ static ArrayObject *getOwnPropertyNames(ExecutionEngine *v4, const Value &o);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4property_p.h b/src/qml/qml/v4/qv4property_p.h
new file mode 100644
index 0000000000..024ad3c720
--- /dev/null
+++ b/src/qml/qml/v4/qv4property_p.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4PROPERTYDESCRIPTOR_H
+#define QV4PROPERTYDESCRIPTOR_H
+
+#include "qv4global_p.h"
+#include "qv4value_p.h"
+#include "qv4internalclass_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct FunctionObject;
+
+struct Property {
+ union {
+ Value value;
+ struct {
+ FunctionObject *get;
+ FunctionObject *set;
+ };
+ };
+
+ // Section 8.10
+ inline void fullyPopulated(PropertyAttributes *attrs) {
+ if (!attrs->hasType()) {
+ value = Value::undefinedValue();
+ }
+ if (attrs->type() == PropertyAttributes::Accessor) {
+ attrs->clearWritable();
+ if (get == (FunctionObject *)0x1)
+ get = 0;
+ if (set == (FunctionObject *)0x1)
+ set = 0;
+ }
+ attrs->resolve();
+ }
+
+ static inline Property fromValue(Value v) {
+ Property pd;
+ pd.value = v;
+ return pd;
+ }
+ static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) {
+ Property pd;
+ pd.get = getter;
+ pd.set = setter;
+ return pd;
+ }
+
+ static Property genericDescriptor() {
+ Property pd;
+ pd.value = Value::emptyValue();
+ return pd;
+ }
+
+ inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const;
+ inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs);
+
+ inline FunctionObject *getter() const { return get; }
+ inline FunctionObject *setter() const { return set; }
+ inline void setGetter(FunctionObject *g) { get = g; }
+ inline void setSetter(FunctionObject *s) { set = s; }
+};
+
+inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const
+{
+ if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type())
+ return false;
+ if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable())
+ return false;
+ if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable())
+ return false;
+ if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable())
+ return false;
+ if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value))
+ return false;
+ if (attrs.type() == PropertyAttributes::Accessor) {
+ if (get != other.get)
+ return false;
+ if (set != other.set)
+ return false;
+ }
+ return true;
+}
+
+inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs)
+{
+ if (otherAttrs.hasEnumerable())
+ attrs.setEnumerable(otherAttrs.isEnumerable());
+ if (otherAttrs.hasConfigurable())
+ attrs.setConfigurable(otherAttrs.isConfigurable());
+ if (otherAttrs.hasWritable())
+ attrs.setWritable(otherAttrs.isWritable());
+ if (otherAttrs.type() == PropertyAttributes::Accessor) {
+ attrs.setType(PropertyAttributes::Accessor);
+ if (other.get)
+ get = (other.get == (FunctionObject *)0x1) ? 0 : other.get;
+ if (other.set)
+ set = (other.set == (FunctionObject *)0x1) ? 0 : other.set;
+ } else if (otherAttrs.type() == PropertyAttributes::Data){
+ attrs.setType(PropertyAttributes::Data);
+ value = other.value;
+ }
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4qmlextensions.cpp b/src/qml/qml/v4/qv4qmlextensions.cpp
new file mode 100644
index 0000000000..a55330ff67
--- /dev/null
+++ b/src/qml/qml/v4/qv4qmlextensions.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4qmlextensions_p.h"
+#include "qv4object_p.h"
+
+using namespace QV4;
+
+void QmlExtensions::markObjects()
+{
+ if (valueTypeWrapperPrototype)
+ valueTypeWrapperPrototype->mark();
+}
diff --git a/src/qml/qml/v4/qv4qmlextensions_p.h b/src/qml/qml/v4/qv4qmlextensions_p.h
new file mode 100644
index 0000000000..cf9e287efe
--- /dev/null
+++ b/src/qml/qml/v4/qv4qmlextensions_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4QMLEXTENSIONS_P_H
+#define QV4QMLEXTENSIONS_P_H
+
+#include <qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+struct Object;
+
+struct Q_QML_EXPORT QmlExtensions
+{
+ QmlExtensions()
+ : valueTypeWrapperPrototype(0)
+ {}
+
+ Object *valueTypeWrapperPrototype;
+
+ void markObjects();
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4qobjectwrapper.cpp b/src/qml/qml/v4/qv4qobjectwrapper.cpp
new file mode 100644
index 0000000000..f79675845b
--- /dev/null
+++ b/src/qml/qml/v4/qv4qobjectwrapper.cpp
@@ -0,0 +1,1792 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4qobjectwrapper_p.h"
+
+#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qjsvalue_p.h>
+#include <private/qqmlaccessors_p.h>
+#include <private/qqmlexpression_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmltypewrapper_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qqmlcontextwrapper_p.h>
+#include <private/qqmllistwrapper_p.h>
+#include <private/qv8engine_p.h>
+
+#include <private/qv4functionobject_p.h>
+#include <private/qv4runtime_p.h>
+#include <private/qv4variantobject_p.h>
+#include <private/qv4sequenceobject_p.h>
+#include <private/qv4objectproto_p.h>
+#include <private/qv4jsonobject_p.h>
+#include <private/qv4regexpobject_p.h>
+
+#include <QtQml/qjsvalue.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+// The code in this file does not violate strict aliasing, but GCC thinks it does
+// so turn off the warnings for us to have a clean build
+# pragma GCC diagnostic ignored "-Wstrict-aliasing"
+# endif
+#endif
+
+
+using namespace QV4;
+
+static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function)
+{
+ if (function && function->subtype == QV4::FunctionObject::WrappedQtMethod) {
+ QObjectMethod *method = static_cast<QObjectMethod*>(function);
+ return qMakePair(method->object(), method->methodIndex());
+ }
+
+ return qMakePair((QObject *)0, -1);
+}
+
+static QPair<QObject *, int> extractQtSignal(const Value &value)
+{
+ if (QV4::FunctionObject *function = value.asFunctionObject())
+ return extractQtMethod(function);
+
+ if (QV4::QmlSignalHandler *handler = value.as<QV4::QmlSignalHandler>())
+ return qMakePair(handler->object(), handler->signalIndex());
+
+ return qMakePair((QObject *)0, -1);
+}
+
+
+struct ReadAccessor {
+ static inline void Indirect(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(n == 0);
+ Q_UNUSED(n);
+
+ void *args[] = { output, 0 };
+ QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
+ }
+
+ static inline void Direct(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(n == 0);
+ Q_UNUSED(n);
+
+ void *args[] = { output, 0 };
+ object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
+ }
+
+ static inline void Accessor(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(property.accessors);
+
+ property.accessors->read(object, property.accessorData, output);
+ if (n) property.accessors->notifier(object, property.accessorData, n);
+ }
+};
+
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, int v)
+{ return QV4::Value::fromInt32(v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, uint v)
+{ return QV4::Value::fromUInt32(v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, bool v)
+{ return QV4::Value::fromBoolean(v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, const QString &v)
+{ return QV4::Value::fromString(e, v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, float v)
+{ return QV4::Value::fromDouble(v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, double v)
+{ return QV4::Value::fromDouble(v); }
+static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, QObject *v)
+{ return QV4::QObjectWrapper::wrap(e, v); }
+
+// Load value properties
+template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
+ void *, QQmlNotifier **)>
+static QV4::Value LoadProperty(QV8Engine *engine, QObject *object,
+ const QQmlPropertyData &property,
+ QQmlNotifier **notifier)
+{
+ Q_ASSERT(!property.isFunction());
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+
+ if (property.isQObject()) {
+ QObject *rv = 0;
+ ReadFunction(object, property, &rv, notifier);
+ return QV4::QObjectWrapper::wrap(v4, rv);
+ } else if (property.isQList()) {
+ return QmlListWrapper::create(engine, object, property.coreIndex, property.propType);
+ } else if (property.propType == QMetaType::QReal) {
+ qreal v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::Int || property.isEnum()) {
+ int v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::Bool) {
+ bool v = false;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::QString) {
+ QString v;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::UInt) {
+ uint v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::Float) {
+ float v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.propType == QMetaType::Double) {
+ double v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(v4, v);
+ } else if (property.isV4Handle()) {
+ QQmlV4Handle handle;
+ ReadFunction(object, property, &handle, notifier);
+ return handle.toValue();
+ } else if (property.propType == qMetaTypeId<QJSValue>()) {
+ QJSValue v;
+ ReadFunction(object, property, &v, notifier);
+ return QJSValuePrivate::get(v)->getValue(v4);
+ } else if (property.isQVariant()) {
+ QVariant v;
+ ReadFunction(object, property, &v, notifier);
+
+ if (QQmlValueTypeFactory::isValueType(v.userType())) {
+ if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType()))
+ return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); // VariantReference value-type.
+ }
+
+ return engine->fromVariant(v);
+ } else if (QQmlValueTypeFactory::isValueType(property.propType)) {
+ Q_ASSERT(notifier == 0);
+
+ if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType))
+ return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType);
+ } else {
+ Q_ASSERT(notifier == 0);
+
+ // see if it's a sequence type
+ bool succeeded = false;
+ QV4::Value retn = QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded);
+ if (succeeded)
+ return retn;
+ }
+
+ if (property.propType == QMetaType::UnknownType) {
+ QMetaProperty p = object->metaObject()->property(property.coreIndex);
+ qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
+ "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
+ return QV4::Value::undefinedValue();
+ } else {
+ QVariant v(property.propType, (void *)0);
+ ReadFunction(object, property, v.data(), notifier);
+ return engine->fromVariant(v);
+ }
+}
+
+QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object)
+ : Object(engine)
+ , m_object(object)
+{
+ vtbl = &static_vtbl;
+ prototype = engine->objectPrototype;
+
+ m_destroy = engine->newIdentifier(QStringLiteral("destroy"));
+ m_toString = engine->newIdentifier(QStringLiteral("toString"));
+}
+
+void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
+{
+ engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("connect"), method_connect);
+ engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("disconnect"), method_disconnect);
+}
+
+QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const
+{
+ QQmlData *ddata = QQmlData::get(m_object, false);
+ if (!ddata)
+ return 0;
+ QQmlPropertyData *result = 0;
+ if (ddata && ddata->propertyCache)
+ result = ddata->propertyCache->property(name, m_object, qmlContext);
+ if (!result)
+ result = QQmlPropertyCache::property(engine->v8Engine->engine(), m_object, name, qmlContext, *local);
+ return result;
+}
+
+Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports)
+{
+ if (QQmlData::wasDeleted(m_object)) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue();
+ }
+
+ if (name->isEqualTo(m_destroy) || name->isEqualTo(m_toString)) {
+ int index = name->isEqualTo(m_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
+ QV4::Value method = QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, index);
+ if (hasProperty)
+ *hasProperty = true;
+ return method;
+ }
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = findProperty(ctx->engine, qmlContext, name, revisionMode, &local);
+
+ if (!result) {
+ if (includeImports && name->startsWithUpper()) {
+ // Check for attached properties
+ if (qmlContext && qmlContext->imports) {
+ QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
+
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (r.isValid()) {
+ if (r.scriptIndex != -1) {
+ return QV4::Value::undefinedValue();
+ } else if (r.type) {
+ return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, r.type, QmlTypeWrapper::ExcludeEnums);
+ } else if (r.importNamespace) {
+ return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, qmlContext->imports, r.importNamespace, QmlTypeWrapper::ExcludeEnums);
+ }
+ Q_ASSERT(!"Unreachable");
+ }
+ }
+ }
+ return QV4::Object::get(this, name, hasProperty);
+ }
+
+ QQmlData::flushPendingBinding(m_object, result->coreIndex);
+ QQmlData *ddata = QQmlData::get(m_object, false);
+
+ if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
+ if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue();
+ }
+ }
+
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (result->isFunction() && !result->isVarProperty()) {
+ if (result->isVMEFunction()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object);
+ Q_ASSERT(vmemo);
+ return vmemo->vmeMethod(result->coreIndex);
+ } else if (result->isV4Function()) {
+ return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex, QV4::Value::fromObject(ctx->engine->qmlContextObject()));
+ } else if (result->isSignalHandler()) {
+ QV4::QmlSignalHandler *handler = new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, result->coreIndex);
+
+ QV4::String *connect = ctx->engine->newIdentifier(QStringLiteral("connect"));
+ QV4::String *disconnect = ctx->engine->newIdentifier(QStringLiteral("disconnect"));
+ handler->put(connect, ctx->engine->functionPrototype->get(connect));
+ handler->put(disconnect, ctx->engine->functionPrototype->get(disconnect));
+
+ return QV4::Value::fromObject(handler);
+ } else {
+ return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex);
+ }
+ }
+
+ QQmlEnginePrivate *ep = ctx->engine->v8Engine->engine() ? QQmlEnginePrivate::get(ctx->engine->v8Engine->engine()) : 0;
+
+ if (result->hasAccessors()) {
+ QQmlNotifier *n = 0;
+ QQmlNotifier **nptr = 0;
+
+ if (ep && ep->propertyCapture && result->accessors->notifier)
+ nptr = &n;
+
+ QV4::Value rv = LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, m_object, *result, nptr);
+
+ if (result->accessors->notifier) {
+ if (n) ep->captureProperty(n);
+ } else {
+ ep->captureProperty(m_object, result->coreIndex, result->notifyIndex);
+ }
+
+ return rv;
+ }
+
+ if (ep && !result->isConstant())
+ ep->captureProperty(m_object, result->coreIndex, result->notifyIndex);
+
+ if (result->isVarProperty()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object);
+ Q_ASSERT(vmemo);
+ return vmemo->vmeProperty(result->coreIndex);
+ } else if (result->isDirect()) {
+ return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, m_object, *result, 0);
+ } else {
+ return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, m_object, *result, 0);
+ }
+}
+
+Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
+{
+ if (QQmlData::wasDeleted(object)) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::nullValue();
+ }
+
+ if (!QQmlData::get(object, true)) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::nullValue();
+ }
+
+ QObjectWrapper *wrapper = wrap(ctx->engine, object).as<QV4::QObjectWrapper>();
+ if (!wrapper) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::nullValue();
+ }
+ return wrapper->getQmlProperty(ctx, qmlContext, name, revisionMode, hasProperty);
+}
+
+bool QObjectWrapper::setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value)
+{
+ if (QQmlData::wasDeleted(object))
+ return false;
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = 0;
+ {
+ result = QQmlPropertyCache::property(ctx->engine->v8Engine->engine(), object, name, qmlContext, local);
+ }
+
+ if (!result)
+ return false;
+
+ if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
+ QQmlData *ddata = QQmlData::get(object);
+ if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
+ return false;
+ }
+
+ if (!result->isWritable() && !result->isQList()) {
+ QString error = QLatin1String("Cannot assign to read-only property \"") +
+ name->toQString() + QLatin1Char('\"');
+ ctx->throwTypeError(error);
+ }
+
+ QQmlBinding *newBinding = 0;
+ if (FunctionObject *f = value.asFunctionObject()) {
+ if (!f->bindingKeyFlag) {
+ if (!result->isVarProperty() && result->propType != qMetaTypeId<QJSValue>()) {
+ // assigning a JS function to a non var or QJSValue property or is not allowed.
+ QString error = QLatin1String("Cannot assign JavaScript function to ");
+ if (!QMetaType::typeName(result->propType))
+ error += QLatin1String("[unknown property type]");
+ else
+ error += QLatin1String(QMetaType::typeName(result->propType));
+ ctx->throwError(error);
+ }
+ } else {
+ // binding assignment.
+ QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine);
+
+ QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame();
+
+ newBinding = new QQmlBinding(value, object, callingQmlContext, frame.source,
+ qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column));
+ newBinding->setTarget(object, *result, callingQmlContext);
+ newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
+ QQmlBinding::RequiresThisObject);
+ }
+ }
+
+ QQmlAbstractBinding *oldBinding =
+ QQmlPropertyPrivate::setBinding(object, result->coreIndex, -1, newBinding);
+ if (oldBinding)
+ oldBinding->destroy();
+
+ if (!newBinding && result->isVarProperty()) {
+ // allow assignment of "special" values (null, undefined, function) to var properties
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
+ Q_ASSERT(vmemo);
+ vmemo->setVMEProperty(result->coreIndex, value);
+ return true;
+ }
+
+#define PROPERTY_STORE(cpptype, value) \
+ cpptype o = value; \
+ int status = -1; \
+ int flags = 0; \
+ void *argv[] = { &o, 0, &status, &flags }; \
+ QMetaObject::metacall(object, QMetaObject::WriteProperty, result->coreIndex, argv);
+
+ if (value.isNull() && result->isQObject()) {
+ PROPERTY_STORE(QObject*, 0);
+ } else if (value.isUndefined() && result->isResettable()) {
+ void *a[] = { 0 };
+ QMetaObject::metacall(object, QMetaObject::ResetProperty, result->coreIndex, a);
+ } else if (value.isUndefined() && result->propType == qMetaTypeId<QVariant>()) {
+ PROPERTY_STORE(QVariant, QVariant());
+ } else if (value.isUndefined() && result->propType == QMetaType::QJsonValue) {
+ PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
+ } else if (!newBinding && result->propType == qMetaTypeId<QJSValue>()) {
+ PROPERTY_STORE(QJSValue, new QJSValuePrivate(ctx->engine, value));
+ } else if (value.isUndefined()) {
+ QString error = QLatin1String("Cannot assign [undefined] to ");
+ if (!QMetaType::typeName(result->propType))
+ error += QLatin1String("[unknown property type]");
+ else
+ error += QLatin1String(QMetaType::typeName(result->propType));
+ ctx->throwError(error);
+ } else if (value.asFunctionObject()) {
+ // this is handled by the binding creation above
+ } else if (result->propType == QMetaType::Int && value.isNumber()) {
+ PROPERTY_STORE(int, qRound(value.asDouble()));
+ } else if (result->propType == QMetaType::QReal && value.isNumber()) {
+ PROPERTY_STORE(qreal, qreal(value.asDouble()));
+ } else if (result->propType == QMetaType::Float && value.isNumber()) {
+ PROPERTY_STORE(float, float(value.asDouble()));
+ } else if (result->propType == QMetaType::Double && value.isNumber()) {
+ PROPERTY_STORE(double, double(value.asDouble()));
+ } else if (result->propType == QMetaType::QString && value.isString()) {
+ PROPERTY_STORE(QString, value.toQString());
+ } else if (result->isVarProperty()) {
+ QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
+ Q_ASSERT(vmemo);
+ vmemo->setVMEProperty(result->coreIndex, value);
+ } else {
+ QVariant v;
+ if (result->isQList())
+ v = ctx->engine->v8Engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
+ else
+ v = ctx->engine->v8Engine->toVariant(value, result->propType);
+
+ QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine);
+ if (!QQmlPropertyPrivate::write(object, *result, v, callingQmlContext)) {
+ const char *valueType = 0;
+ if (v.userType() == QVariant::Invalid) valueType = "null";
+ else valueType = QMetaType::typeName(v.userType());
+
+ const char *targetTypeName = QMetaType::typeName(result->propType);
+ if (!targetTypeName)
+ targetTypeName = "an unregistered type";
+
+ QString error = QLatin1String("Cannot assign ") +
+ QLatin1String(valueType) +
+ QLatin1String(" to ") +
+ QLatin1String(targetTypeName);
+ ctx->throwError(error);
+ }
+ }
+
+ return true;
+}
+
+Value QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
+{
+ if (QQmlData::wasDeleted(object))
+ return QV4::Value::nullValue();
+
+ QQmlData *ddata = QQmlData::get(object, true);
+ if (!ddata)
+ return QV4::Value::undefinedValue();
+
+ if (ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isEmpty()) {
+ // We own the JS object
+ return ddata->jsWrapper.value();
+ } else if (ddata->jsWrapper.isEmpty() &&
+ (ddata->jsEngineId == engine->m_engineId || // We own the QObject
+ ddata->jsEngineId == 0 || // No one owns the QObject
+ !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
+
+ QV4::Value rv = create(engine, ddata, object);
+ ddata->jsWrapper = rv;
+ ddata->jsEngineId = engine->m_engineId;
+ return rv;
+
+ } else {
+ // If this object is tainted, we have to check to see if it is in our
+ // tainted object list
+ Object *alternateWrapper = 0;
+ if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV8Object)
+ alternateWrapper = engine->m_multiplyWrappedQObjects->value(object);
+
+ // If our tainted handle doesn't exist or has been collected, and there isn't
+ // a handle in the ddata, we can assume ownership of the ddata->v8object
+ if (ddata->jsWrapper.isEmpty() && !alternateWrapper) {
+ QV4::Value result = create(engine, ddata, object);
+ ddata->jsWrapper = result;
+ ddata->jsEngineId = engine->m_engineId;
+ return result;
+ }
+
+ if (!alternateWrapper) {
+ alternateWrapper = create(engine, ddata, object).asObject();
+ if (!engine->m_multiplyWrappedQObjects)
+ engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
+ engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper);
+ ddata->hasTaintedV8Object = true;
+ }
+
+ return QV4::Value::fromObject(alternateWrapper);
+ }
+}
+
+QV4::Value QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object)
+{
+ QQmlEngine *qmlEngine = engine->v8Engine->engine();
+ if (!ddata->propertyCache && qmlEngine) {
+ ddata->propertyCache = QQmlEnginePrivate::get(qmlEngine)->cache(object);
+ if (ddata->propertyCache) ddata->propertyCache->addref();
+ }
+
+ return Value::fromObject(new (engine->memoryManager) QV4::QObjectWrapper(engine, object));
+}
+
+QV4::Value QObjectWrapper::get(Managed *m, String *name, bool *hasProperty)
+{
+ QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
+ ExecutionEngine *v4 = m->engine();
+ QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4);
+ return that->getQmlProperty(v4->current, qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true);
+}
+
+void QObjectWrapper::put(Managed *m, String *name, const Value &value)
+{
+ QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
+ ExecutionEngine *v4 = m->engine();
+
+ if (QQmlData::wasDeleted(that->m_object))
+ return;
+
+ QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4);
+ if (!setQmlProperty(v4->current, qmlContext, that->m_object, name, QV4::QObjectWrapper::IgnoreRevision, value)) {
+ QString error = QLatin1String("Cannot assign to non-existent property \"") +
+ name->toQString() + QLatin1Char('\"');
+ v4->current->throwError(error);
+ }
+}
+
+PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
+{
+ const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
+ ExecutionEngine *engine = that->engine();
+ QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine);
+ QQmlPropertyData local;
+ if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local)
+ || name->isEqualTo(that->m_destroy) || name->isEqualTo(that->m_toString))
+ return QV4::Attr_Data;
+ else
+ return QV4::Object::query(m, name);
+}
+
+Property *QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes)
+{
+ *name = 0;
+ *index = UINT_MAX;
+
+ QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
+
+ if (!that->m_object)
+ return QV4::Object::advanceIterator(m, it, name, index, attributes);
+
+ const QMetaObject *mo = that->m_object->metaObject();
+ const int propertyCount = mo->propertyCount();
+ if (it->arrayIndex < propertyCount) {
+ *name = that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()));
+ ++it->arrayIndex;
+ if (attributes)
+ *attributes = QV4::Attr_Data;
+ it->tmpDynamicProperty.value = that->get(*name);
+ return &it->tmpDynamicProperty;
+ }
+ const int methodCount = mo->methodCount();
+ if (it->arrayIndex < propertyCount + methodCount) {
+ *name = that->engine()->newString(QString::fromUtf8(mo->method(it->arrayIndex - propertyCount).name()));
+ ++it->arrayIndex;
+ if (attributes)
+ *attributes = QV4::Attr_Data;
+ it->tmpDynamicProperty.value = that->get(*name);
+ return &it->tmpDynamicProperty;
+ }
+ return QV4::Object::advanceIterator(m, it, name, index, attributes);
+}
+
+namespace QV4 {
+
+struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
+{
+ QV4::PersistentValue function;
+ QV4::PersistentValue thisObject;
+ int signalIndex;
+
+ QObjectSlotDispatcher()
+ : QtPrivate::QSlotObjectBase(&impl)
+ , signalIndex(-1)
+ {}
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret)
+ {
+ switch (which) {
+ case Destroy: {
+ delete static_cast<QObjectSlotDispatcher*>(this_);
+ }
+ break;
+ case Call: {
+ QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
+ QVarLengthArray<int, 9> dummy;
+ int *argsTypes = QQmlPropertyCache::methodParameterTypes(r, This->signalIndex, dummy, 0);
+
+ int argCount = argsTypes ? argsTypes[0]:0;
+
+ QV4::FunctionObject *f = This->function.value().asFunctionObject();
+ QV4::ExecutionEngine *v4 = f->internalClass->engine;
+ QV4::ExecutionContext *ctx = v4->current;
+
+ QVarLengthArray<QV4::Value, 9> args(argCount);
+ for (int ii = 0; ii < argCount; ++ii) {
+ int type = argsTypes[ii + 1];
+ if (type == qMetaTypeId<QVariant>()) {
+ args[ii] = v4->v8Engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
+ } else {
+ args[ii] = v4->v8Engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
+ }
+ }
+
+ try {
+ f->call(This->thisObject.isEmpty() ? Value::fromObject(v4->globalObject) : This->thisObject.value(), args.data(), argCount);
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ QQmlError error;
+ QQmlExpressionPrivate::exceptionToError(e, error);
+ if (error.description().isEmpty())
+ error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(f->name->toQString()));
+ QQmlEnginePrivate::get(v4->v8Engine->engine())->warning(error);
+ }
+ }
+ break;
+ case Compare: {
+ QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
+ if (connection->function.isEmpty()) {
+ *ret = false;
+ return;
+ }
+
+ // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
+ // for the new-style QObject::connect. Here we use the engine pointer as sentinel
+ // to distinguish those type of QSlotObjectBase connections from our QML connections.
+ QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]);
+ if (v4 != connection->function.engine()) {
+ *ret = false;
+ return;
+ }
+
+ QV4::Value function = *reinterpret_cast<QV4::Value*>(metaArgs[1]);
+ QV4::Value thisObject = *reinterpret_cast<QV4::Value*>(metaArgs[2]);
+ QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
+ int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
+
+ if (slotIndexToDisconnect != -1) {
+ // This is a QObject function wrapper
+ if (connection->thisObject.isEmpty() == thisObject.isEmpty() &&
+ (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) {
+
+ QPair<QObject *, int> connectedFunctionData = extractQtMethod(connection->function.value().asFunctionObject());
+ if (connectedFunctionData.first == receiverToDisconnect &&
+ connectedFunctionData.second == slotIndexToDisconnect) {
+ *ret = true;
+ return;
+ }
+ }
+ } else {
+ // This is a normal JS function
+ if (__qmljs_strict_equal(connection->function, function) &&
+ connection->thisObject.isEmpty() == thisObject.isEmpty() &&
+ (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) {
+ *ret = true;
+ return;
+ }
+ }
+
+ *ret = false;
+ }
+ break;
+ case NumOperations:
+ break;
+ }
+ };
+};
+
+} // namespace QV4
+
+Value QObjectWrapper::method_connect(SimpleCallContext *ctx)
+{
+ if (ctx->argumentCount == 0)
+ V4THROW_ERROR("Function.prototype.connect: no arguments given");
+
+ QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject);
+ QObject *signalObject = signalInfo.first;
+ int signalIndex = signalInfo.second;
+
+ if (signalIndex < 0)
+ V4THROW_ERROR("Function.prototype.connect: this object is not a signal");
+
+ if (!signalObject)
+ V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
+
+ if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
+ V4THROW_ERROR("Function.prototype.connect: this object is not a signal");
+
+ QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher;
+ slot->signalIndex = signalIndex;
+
+ if (ctx->argumentCount == 1) {
+ slot->function = ctx->arguments[0];
+ } else if (ctx->argumentCount >= 2) {
+ slot->thisObject = ctx->arguments[0];
+ slot->function = ctx->arguments[1];
+ }
+
+ if (!slot->function.value().asFunctionObject())
+ V4THROW_ERROR("Function.prototype.connect: target is not a function");
+
+ if (!slot->thisObject.isEmpty() && !slot->thisObject.value().isObject())
+ V4THROW_ERROR("Function.prototype.connect: target this is not an object");
+
+ QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection);
+
+ return QV4::Value::undefinedValue();
+}
+
+Value QObjectWrapper::method_disconnect(SimpleCallContext *ctx)
+{
+ if (ctx->argumentCount == 0)
+ V4THROW_ERROR("Function.prototype.disconnect: no arguments given");
+
+ QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject);
+ QObject *signalObject = signalInfo.first;
+ int signalIndex = signalInfo.second;
+
+ if (signalIndex == -1)
+ V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
+
+ if (!signalObject)
+ V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
+
+ if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
+ V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
+
+ QV4::Value functionValue = QV4::Value::emptyValue();
+ QV4::Value functionThisValue = QV4::Value::emptyValue();
+
+ if (ctx->argumentCount == 1) {
+ functionValue = ctx->arguments[0];
+ } else if (ctx->argumentCount >= 2) {
+ functionThisValue = ctx->arguments[0];
+ functionValue = ctx->arguments[1];
+ }
+
+ if (!functionValue.asFunctionObject())
+ V4THROW_ERROR("Function.prototype.disconnect: target is not a function");
+
+ if (!functionThisValue.isEmpty() && !functionThisValue.isObject())
+ V4THROW_ERROR("Function.prototype.disconnect: target this is not an object");
+
+ QPair<QObject *, int> functionData = extractQtMethod(functionValue.asFunctionObject());
+
+ void *a[] = {
+ ctx->engine,
+ &functionValue,
+ &functionThisValue,
+ functionData.first,
+ &functionData.second
+ };
+
+ QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a));
+
+ return QV4::Value::undefinedValue();
+}
+
+static void markChildQObjectsRecursively(QObject *parent)
+{
+ const QObjectList &children = parent->children();
+ for (int i = 0; i < children.count(); ++i) {
+ QObject *child = children.at(i);
+ QQmlData *ddata = QQmlData::get(child, /*create*/false);
+ if (ddata)
+ ddata->jsWrapper.markOnce();
+ markChildQObjectsRecursively(child);
+ }
+}
+
+void QObjectWrapper::markObjects(Managed *that)
+{
+ QObjectWrapper *This = static_cast<QObjectWrapper*>(that);
+
+ if (QObject *o = This->m_object.data()) {
+ QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
+ if (vme)
+ vme->mark();
+
+ // Children usually don't need to be marked, the gc keeps them alive.
+ // But in the rare case of a "floating" QObject without a parent that
+ // _gets_ marked (we've been called here!) then we also need to
+ // propagate the marking down to the children recursively.
+ if (!o->parent())
+ markChildQObjectsRecursively(o);
+ }
+
+ QV4::Object::markObjects(that);
+}
+
+namespace {
+ struct QObjectDeleter : public QV4::GCDeletable
+ {
+ QObjectDeleter(QObject *o)
+ : m_objectToDelete(o)
+ {}
+ ~QObjectDeleter()
+ {
+ QQmlData *ddata = QQmlData::get(m_objectToDelete, false);
+ if (ddata && ddata->ownContext && ddata->context)
+ ddata->context->emitDestruction();
+ // This object is notionally destroyed now
+ ddata->isQueuedForDeletion = true;
+ if (lastCall)
+ delete m_objectToDelete;
+ else
+ m_objectToDelete->deleteLater();
+ }
+
+ QObject *m_objectToDelete;
+ };
+}
+
+void QObjectWrapper::collectDeletables(Managed *m, GCDeletable **deletable)
+{
+ QObjectWrapper *This = static_cast<QObjectWrapper*>(m);
+ QPointer<QObject> &object = This->m_object;
+ if (!object)
+ return;
+
+ QQmlData *ddata = QQmlData::get(object, false);
+ if (!ddata)
+ return;
+
+ if (object->parent() || ddata->indestructible)
+ return;
+
+ QObjectDeleter *deleter = new QObjectDeleter(object);
+ object = 0;
+ deleter->next = *deletable;
+ *deletable = deleter;
+}
+
+DEFINE_MANAGED_VTABLE_WITH_DELETABLES(QObjectWrapper);
+
+// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
+// correctly in a worker thread
+
+namespace {
+
+template<typename A, typename B, typename C, typename D, typename E,
+ typename F, typename G, typename H>
+class MaxSizeOf8 {
+ template<typename Z, typename X>
+ struct SMax {
+ char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
+ };
+public:
+ static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
+};
+
+struct CallArgument {
+ inline CallArgument();
+ inline ~CallArgument();
+ inline void *dataPtr();
+
+ inline void initAsType(int type);
+ inline void fromValue(int type, QV8Engine *, const QV4::Value&);
+ inline QV4::Value toValue(QV8Engine *);
+
+private:
+ CallArgument(const CallArgument &);
+
+ inline void cleanup();
+
+ union {
+ float floatValue;
+ double doubleValue;
+ quint32 intValue;
+ bool boolValue;
+ QObject *qobjectPtr;
+
+ char allocData[MaxSizeOf8<QVariant,
+ QString,
+ QList<QObject *>,
+ QJSValue,
+ QQmlV4Handle,
+ QJsonArray,
+ QJsonObject,
+ QJsonValue>::Size];
+ qint64 q_for_alignment;
+ };
+
+ // Pointers to allocData
+ union {
+ QString *qstringPtr;
+ QVariant *qvariantPtr;
+ QList<QObject *> *qlistPtr;
+ QJSValue *qjsValuePtr;
+ QQmlV4Handle *handlePtr;
+ QJsonArray *jsonArrayPtr;
+ QJsonObject *jsonObjectPtr;
+ QJsonValue *jsonValuePtr;
+ };
+
+ int type;
+};
+}
+
+namespace {
+struct CallArgs
+{
+ CallArgs(int length, QV4::Value *args) : _length(length), _args(args) {}
+ int Length() const { return _length; }
+ QV4::Value operator[](int idx) { return _args[idx]; }
+
+private:
+ int _length;
+ QV4::Value *_args;
+};
+}
+
+static QV4::Value CallMethod(QObject *object, int index, int returnType, int argCount,
+ int *argTypes, QV8Engine *engine, CallArgs &callArgs)
+{
+ if (argCount > 0) {
+
+ // Special handling is required for value types.
+ // We need to save the current value in a temporary,
+ // and reapply it after converting all arguments.
+ // This avoids the "overwriting copy-value-type-value"
+ // problem during Q_INVOKABLE function invocation.
+ QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
+ QVariant valueTypeValue;
+ if (valueTypeObject)
+ valueTypeValue = valueTypeObject->value();
+
+ // Convert all arguments.
+ QVarLengthArray<CallArgument, 9> args(argCount + 1);
+ args[0].initAsType(returnType);
+ for (int ii = 0; ii < argCount; ++ii)
+ args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
+ QVarLengthArray<void *, 9> argData(args.count());
+ for (int ii = 0; ii < args.count(); ++ii)
+ argData[ii] = args[ii].dataPtr();
+
+ // Reinstate saved value type object value if required.
+ if (valueTypeObject)
+ valueTypeObject->setValue(valueTypeValue);
+
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
+
+ return args[0].toValue(engine);
+
+ } else if (returnType != QMetaType::Void) {
+
+ CallArgument arg;
+ arg.initAsType(returnType);
+
+ void *args[] = { arg.dataPtr() };
+
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
+
+ return arg.toValue(engine);
+
+ } else {
+
+ void *args[] = { 0 };
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
+ return QV4::Value::undefinedValue();
+
+ }
+}
+
+/*!
+ Returns the match score for converting \a actual to be of type \a conversionType. A
+ zero score means "perfect match" whereas a higher score is worse.
+
+ The conversion table is copied out of the \l QScript::callQtMethod() function.
+*/
+static int MatchScore(const QV4::Value &actual, int conversionType)
+{
+ if (actual.isNumber()) {
+ switch (conversionType) {
+ case QMetaType::Double:
+ return 0;
+ case QMetaType::Float:
+ return 1;
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ return 2;
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ return 3;
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ return 4;
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ return 5;
+ break;
+ case QMetaType::Char:
+ case QMetaType::UChar:
+ return 6;
+ case QMetaType::QJsonValue:
+ return 5;
+ default:
+ return 10;
+ }
+ } else if (actual.isString()) {
+ switch (conversionType) {
+ case QMetaType::QString:
+ return 0;
+ case QMetaType::QJsonValue:
+ return 5;
+ default:
+ return 10;
+ }
+ } else if (actual.isBoolean()) {
+ switch (conversionType) {
+ case QMetaType::Bool:
+ return 0;
+ case QMetaType::QJsonValue:
+ return 5;
+ default:
+ return 10;
+ }
+ } else if (actual.asDateObject()) {
+ switch (conversionType) {
+ case QMetaType::QDateTime:
+ return 0;
+ case QMetaType::QDate:
+ return 1;
+ case QMetaType::QTime:
+ return 2;
+ default:
+ return 10;
+ }
+ } else if (actual.as<QV4::RegExpObject>()) {
+ switch (conversionType) {
+ case QMetaType::QRegExp:
+ return 0;
+ default:
+ return 10;
+ }
+ } else if (actual.asArrayObject()) {
+ switch (conversionType) {
+ case QMetaType::QJsonArray:
+ return 3;
+ case QMetaType::QStringList:
+ case QMetaType::QVariantList:
+ return 5;
+ case QMetaType::QVector4D:
+ case QMetaType::QMatrix4x4:
+ return 6;
+ case QMetaType::QVector3D:
+ return 7;
+ default:
+ return 10;
+ }
+ } else if (actual.isNull()) {
+ switch (conversionType) {
+ case QMetaType::VoidStar:
+ case QMetaType::QObjectStar:
+ case QMetaType::QJsonValue:
+ return 0;
+ default: {
+ const char *typeName = QMetaType::typeName(conversionType);
+ if (typeName && typeName[strlen(typeName) - 1] == '*')
+ return 0;
+ else
+ return 10;
+ }
+ }
+ } else if (QV4::Object *obj = actual.asObject()) {
+ QV8Engine *engine = obj->engine()->v8Engine;
+
+ if (QV4::VariantObject *v = obj->as<QV4::VariantObject>()) {
+ if (conversionType == qMetaTypeId<QVariant>())
+ return 0;
+ if (engine->toVariant(actual, -1).userType() == conversionType)
+ return 0;
+ else
+ return 10;
+ }
+
+ if (obj->as<QObjectWrapper>()) {
+ switch (conversionType) {
+ case QMetaType::QObjectStar:
+ return 0;
+ default:
+ return 10;
+ }
+ }
+
+ if (obj->as<QV4::QmlValueTypeWrapper>()) {
+ if (engine->toVariant(actual, -1).userType() == conversionType)
+ return 0;
+ return 10;
+ } else if (conversionType == QMetaType::QJsonObject) {
+ return 5;
+ } else {
+ return 10;
+ }
+
+ } else {
+ return 10;
+ }
+}
+
+static inline int QMetaObject_methods(const QMetaObject *metaObject)
+{
+ struct Private
+ {
+ int revision;
+ int className;
+ int classInfoCount, classInfoData;
+ int methodCount, methodData;
+ };
+
+ return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
+}
+
+/*!
+Returns the next related method, if one, or 0.
+*/
+static const QQmlPropertyData * RelatedMethod(QObject *object,
+ const QQmlPropertyData *current,
+ QQmlPropertyData &dummy)
+{
+ QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
+ if (!current->isOverload())
+ return 0;
+
+ Q_ASSERT(!current->overrideIndexIsProperty);
+
+ if (cache) {
+ return cache->method(current->overrideIndex);
+ } else {
+ const QMetaObject *mo = object->metaObject();
+ int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
+
+ while (methodOffset > current->overrideIndex) {
+ mo = mo->superClass();
+ methodOffset -= QMetaObject_methods(mo);
+ }
+
+ QMetaMethod method = mo->method(current->overrideIndex);
+ dummy.load(method);
+
+ // Look for overloaded methods
+ QByteArray methodName = method.name();
+ for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
+ if (methodName == mo->method(ii).name()) {
+ dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
+ dummy.overrideIndexIsProperty = 0;
+ dummy.overrideIndex = ii;
+ return &dummy;
+ }
+ }
+
+ return &dummy;
+ }
+}
+
+static QV4::Value CallPrecise(QObject *object, const QQmlPropertyData &data,
+ QV8Engine *engine, CallArgs &callArgs)
+{
+ QByteArray unknownTypeError;
+
+ int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
+
+ if (returnType == QMetaType::UnknownType) {
+ QString typeName = QString::fromLatin1(unknownTypeError);
+ QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
+ QV8Engine::getV4(engine)->current->throwError(error);
+ }
+
+ if (data.hasArguments()) {
+
+ int *args = 0;
+ QVarLengthArray<int, 9> dummy;
+
+ args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
+ &unknownTypeError);
+
+ if (!args) {
+ QString typeName = QString::fromLatin1(unknownTypeError);
+ QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
+ QV8Engine::getV4(engine)->current->throwError(error);
+ }
+
+ if (args[0] > callArgs.Length()) {
+ QString error = QLatin1String("Insufficient arguments");
+ QV8Engine::getV4(engine)->current->throwError(error);
+ }
+
+ return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
+
+ } else {
+
+ return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
+
+ }
+}
+
+/*!
+Resolve the overloaded method to call. The algorithm works conceptually like this:
+ 1. Resolve the set of overloads it is *possible* to call.
+ Impossible overloads include those that have too many parameters or have parameters
+ of unknown type.
+ 2. Filter the set of overloads to only contain those with the closest number of
+ parameters.
+ For example, if we are called with 3 parameters and there are 2 overloads that
+ take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
+ 3. Find the best remaining overload based on its match score.
+ If two or more overloads have the same match score, call the last one. The match
+ score is constructed by adding the matchScore() result for each of the parameters.
+*/
+static QV4::Value CallOverloaded(QObject *object, const QQmlPropertyData &data,
+ QV8Engine *engine, CallArgs &callArgs)
+{
+ int argumentCount = callArgs.Length();
+
+ const QQmlPropertyData *best = 0;
+ int bestParameterScore = INT_MAX;
+ int bestMatchScore = INT_MAX;
+
+ // Special handling is required for value types.
+ // We need to save the current value in a temporary,
+ // and reapply it after converting all arguments.
+ // This avoids the "overwriting copy-value-type-value"
+ // problem during Q_INVOKABLE function invocation.
+ QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
+ QVariant valueTypeValue;
+ if (valueTypeObject)
+ valueTypeValue = valueTypeObject->value();
+
+ QQmlPropertyData dummy;
+ const QQmlPropertyData *attempt = &data;
+
+ do {
+ QVarLengthArray<int, 9> dummy;
+ int methodArgumentCount = 0;
+ int *methodArgTypes = 0;
+ if (attempt->hasArguments()) {
+ typedef QQmlPropertyCache PC;
+ int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
+ if (!args) // Must be an unknown argument
+ continue;
+
+ methodArgumentCount = args[0];
+ methodArgTypes = args + 1;
+ }
+
+ if (methodArgumentCount > argumentCount)
+ continue; // We don't have sufficient arguments to call this method
+
+ int methodParameterScore = argumentCount - methodArgumentCount;
+ if (methodParameterScore > bestParameterScore)
+ continue; // We already have a better option
+
+ int methodMatchScore = 0;
+ for (int ii = 0; ii < methodArgumentCount; ++ii)
+ methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
+
+ if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
+ best = attempt;
+ bestParameterScore = methodParameterScore;
+ bestMatchScore = methodMatchScore;
+ }
+
+ if (bestParameterScore == 0 && bestMatchScore == 0)
+ break; // We can't get better than that
+
+ } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
+
+ if (best) {
+ if (valueTypeObject)
+ valueTypeObject->setValue(valueTypeValue);
+ return CallPrecise(object, *best, engine, callArgs);
+ } else {
+ QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
+ const QQmlPropertyData *candidate = &data;
+ while (candidate) {
+ error += QLatin1String("\n ") +
+ QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
+ candidate = RelatedMethod(object, candidate, dummy);
+ }
+
+ QV8Engine::getV4(engine)->current->throwError(error);
+ }
+}
+
+CallArgument::CallArgument()
+: type(QVariant::Invalid)
+{
+}
+
+CallArgument::~CallArgument()
+{
+ cleanup();
+}
+
+void CallArgument::cleanup()
+{
+ if (type == QMetaType::QString) {
+ qstringPtr->~QString();
+ } else if (type == -1 || type == QMetaType::QVariant) {
+ qvariantPtr->~QVariant();
+ } else if (type == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr->~QJSValue();
+ } else if (type == qMetaTypeId<QList<QObject *> >()) {
+ qlistPtr->~QList<QObject *>();
+ } else if (type == QMetaType::QJsonArray) {
+ jsonArrayPtr->~QJsonArray();
+ } else if (type == QMetaType::QJsonObject) {
+ jsonObjectPtr->~QJsonObject();
+ } else if (type == QMetaType::QJsonValue) {
+ jsonValuePtr->~QJsonValue();
+ }
+}
+
+void *CallArgument::dataPtr()
+{
+ if (type == -1)
+ return qvariantPtr->data();
+ else
+ return (void *)&allocData;
+}
+
+void CallArgument::initAsType(int callType)
+{
+ if (type != 0) { cleanup(); type = 0; }
+ if (callType == QMetaType::UnknownType) return;
+
+ if (callType == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue();
+ type = callType;
+ } else if (callType == QMetaType::Int ||
+ callType == QMetaType::UInt ||
+ callType == QMetaType::Bool ||
+ callType == QMetaType::Double ||
+ callType == QMetaType::Float) {
+ type = callType;
+ } else if (callType == QMetaType::QObjectStar) {
+ qobjectPtr = 0;
+ type = callType;
+ } else if (callType == QMetaType::QString) {
+ qstringPtr = new (&allocData) QString();
+ type = callType;
+ } else if (callType == QMetaType::QVariant) {
+ type = callType;
+ qvariantPtr = new (&allocData) QVariant();
+ } else if (callType == qMetaTypeId<QList<QObject *> >()) {
+ type = callType;
+ qlistPtr = new (&allocData) QList<QObject *>();
+ } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
+ type = callType;
+ handlePtr = new (&allocData) QQmlV4Handle;
+ } else if (callType == QMetaType::QJsonArray) {
+ type = callType;
+ jsonArrayPtr = new (&allocData) QJsonArray();
+ } else if (callType == QMetaType::QJsonObject) {
+ type = callType;
+ jsonObjectPtr = new (&allocData) QJsonObject();
+ } else if (callType == QMetaType::QJsonValue) {
+ type = callType;
+ jsonValuePtr = new (&allocData) QJsonValue();
+ } else if (callType == QMetaType::Void) {
+ type = -1;
+ qvariantPtr = new (&allocData) QVariant();
+ } else {
+ type = -1;
+ qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
+ }
+}
+
+void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::Value &value)
+{
+ if (type != 0) { cleanup(); type = 0; }
+
+ if (callType == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue(new QJSValuePrivate(QV8Engine::getV4(engine), value));
+ type = qMetaTypeId<QJSValue>();
+ } else if (callType == QMetaType::Int) {
+ intValue = quint32(value.toInt32());
+ type = callType;
+ } else if (callType == QMetaType::UInt) {
+ intValue = quint32(value.toUInt32());
+ type = callType;
+ } else if (callType == QMetaType::Bool) {
+ boolValue = value.toBoolean();
+ type = callType;
+ } else if (callType == QMetaType::Double) {
+ doubleValue = double(value.toNumber());
+ type = callType;
+ } else if (callType == QMetaType::Float) {
+ floatValue = float(value.toNumber());
+ type = callType;
+ } else if (callType == QMetaType::QString) {
+ if (value.isNull() || value.isUndefined())
+ qstringPtr = new (&allocData) QString();
+ else
+ qstringPtr = new (&allocData) QString(value.toQString());
+ type = callType;
+ } else if (callType == QMetaType::QObjectStar) {
+ qobjectPtr = 0;
+ if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
+ qobjectPtr = qobjectWrapper->object();
+ type = callType;
+ } else if (callType == qMetaTypeId<QVariant>()) {
+ qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
+ type = callType;
+ } else if (callType == qMetaTypeId<QList<QObject*> >()) {
+ qlistPtr = new (&allocData) QList<QObject *>();
+ if (QV4::ArrayObject *array = value.asArrayObject()) {
+ uint32_t length = array->arrayLength();
+ for (uint32_t ii = 0; ii < length; ++ii) {
+ QObject *o = 0;
+ if (QV4::QObjectWrapper *qobjectWrapper = array->getIndexed(ii).as<QV4::QObjectWrapper>())
+ o = qobjectWrapper->object();
+ qlistPtr->append(o);
+ }
+ } else {
+ QObject *o = 0;
+ if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
+ o = qobjectWrapper->object();
+ qlistPtr->append(o);
+ }
+ type = callType;
+ } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
+ handlePtr = new (&allocData) QQmlV4Handle(QQmlV4Handle(value));
+ type = callType;
+ } else if (callType == QMetaType::QJsonArray) {
+ jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(value.asArrayObject()));
+ type = callType;
+ } else if (callType == QMetaType::QJsonObject) {
+ jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(value.asObject()));
+ type = callType;
+ } else if (callType == QMetaType::QJsonValue) {
+ jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value));
+ type = callType;
+ } else if (callType == QMetaType::Void) {
+ *qvariantPtr = QVariant();
+ } else {
+ qvariantPtr = new (&allocData) QVariant();
+ type = -1;
+
+ QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
+ QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
+
+ if (v.userType() == callType) {
+ *qvariantPtr = v;
+ } else if (v.canConvert(callType)) {
+ *qvariantPtr = v;
+ qvariantPtr->convert(callType);
+ } else if (QV4::SequencePrototype::isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
+ // convert the JS array to a sequence of the correct type.
+ QVariant seqV = engine->toVariant(value, callType);
+ *qvariantPtr = seqV;
+ } else {
+ QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
+ if (!mo.isNull()) {
+ QObject *obj = ep->toQObject(v);
+
+ if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
+ obj = 0;
+
+ *qvariantPtr = QVariant(callType, &obj);
+ } else {
+ *qvariantPtr = QVariant(callType, (void *)0);
+ }
+ }
+ }
+}
+
+QV4::Value CallArgument::toValue(QV8Engine *engine)
+{
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ if (type == qMetaTypeId<QJSValue>()) {
+ return QJSValuePrivate::get(*qjsValuePtr)->getValue(v4);
+ } else if (type == QMetaType::Int) {
+ return QV4::Value::fromInt32(int(intValue));
+ } else if (type == QMetaType::UInt) {
+ return QV4::Value::fromUInt32(intValue);
+ } else if (type == QMetaType::Bool) {
+ return QV4::Value::fromBoolean(boolValue);
+ } else if (type == QMetaType::Double) {
+ return QV4::Value::fromDouble(doubleValue);
+ } else if (type == QMetaType::Float) {
+ return QV4::Value::fromDouble(floatValue);
+ } else if (type == QMetaType::QString) {
+ return engine->toString(*qstringPtr);
+ } else if (type == QMetaType::QObjectStar) {
+ QObject *object = qobjectPtr;
+ if (object)
+ QQmlData::get(object, true)->setImplicitDestructible();
+ return QV4::QObjectWrapper::wrap(v4, object);
+ } else if (type == qMetaTypeId<QList<QObject *> >()) {
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
+ QList<QObject *> &list = *qlistPtr;
+ QV4::ArrayObject *array = v4->newArrayObject();
+ array->arrayReserve(list.count());
+ array->arrayDataLen = list.count();
+ for (int ii = 0; ii < list.count(); ++ii)
+ array->arrayData[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii));
+ array->setArrayLengthUnchecked(list.count());
+ return QV4::Value::fromObject(array);
+ } else if (type == qMetaTypeId<QQmlV4Handle>()) {
+ return handlePtr->toValue();
+ } else if (type == QMetaType::QJsonArray) {
+ return QV4::JsonObject::fromJsonArray(v4, *jsonArrayPtr);
+ } else if (type == QMetaType::QJsonObject) {
+ return QV4::JsonObject::fromJsonObject(v4, *jsonObjectPtr);
+ } else if (type == QMetaType::QJsonValue) {
+ return QV4::JsonObject::fromJsonValue(v4, *jsonValuePtr);
+ } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
+ QVariant value = *qvariantPtr;
+ QV4::Value rv = engine->fromVariant(value);
+ if (QV4::QObjectWrapper *qobjectWrapper = rv.as<QV4::QObjectWrapper>()) {
+ if (QObject *object = qobjectWrapper->object())
+ QQmlData::get(object, true)->setImplicitDestructible();
+ }
+ return rv;
+ } else {
+ return QV4::Value::undefinedValue();
+ }
+}
+
+Value QObjectMethod::create(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal)
+{
+ return Value::fromObject(new (scope->engine->memoryManager) QObjectMethod(scope, object, index, qmlGlobal));
+}
+
+QObjectMethod::QObjectMethod(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal)
+ : FunctionObject(scope)
+ , m_object(object)
+ , m_index(index)
+ , m_qmlGlobal(qmlGlobal)
+{
+ vtbl = &static_vtbl;
+ subtype = WrappedQtMethod;
+}
+
+QV4::Value QObjectMethod::method_toString(QV4::ExecutionContext *ctx)
+{
+ QString result;
+ if (m_object) {
+ QString objectName = m_object->objectName();
+
+ result += QString::fromUtf8(m_object->metaObject()->className());
+ result += QLatin1String("(0x");
+ result += QString::number((quintptr)m_object.data(),16);
+
+ if (!objectName.isEmpty()) {
+ result += QLatin1String(", \"");
+ result += objectName;
+ result += QLatin1Char('\"');
+ }
+
+ result += QLatin1Char(')');
+ } else {
+ result = QLatin1String("null");
+ }
+
+ return QV4::Value::fromString(ctx, result);
+}
+
+QV4::Value QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc)
+{
+ if (!m_object)
+ return QV4::Value::undefinedValue();
+ if (QQmlData::keepAliveDuringGarbageCollection(m_object))
+ ctx->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
+
+ int delay = 0;
+ if (argc > 0)
+ delay = args[0].toUInt32();
+
+ if (delay > 0)
+ QTimer::singleShot(delay, m_object, SLOT(deleteLater()));
+ else
+ m_object->deleteLater();
+
+ return QV4::Value::undefinedValue();
+}
+
+Value QObjectMethod::call(Managed *m, const Value &thisObject, Value *args, int argc)
+{
+ QObjectMethod *This = static_cast<QObjectMethod*>(m);
+ return This->callInternal(thisObject, args, argc);
+}
+
+Value QObjectMethod::callInternal(const Value &, Value *args, int argc)
+{
+ ExecutionContext *context = engine()->current;
+ if (m_index == DestroyMethod)
+ return method_destroy(context, args, argc);
+ else if (m_index == ToStringMethod)
+ return method_toString(context);
+
+ QObject *object = m_object.data();
+ if (!object)
+ return QV4::Value::undefinedValue();
+
+ QQmlData *ddata = QQmlData::get(object);
+ if (!ddata)
+ return QV4::Value::undefinedValue();
+
+ QV8Engine *v8Engine = context->engine->v8Engine;
+
+ QQmlPropertyData method;
+
+ if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
+ if (ddata->propertyCache) {
+ QQmlPropertyData *d = ddata->propertyCache->method(m_index);
+ if (!d)
+ return QV4::Value::undefinedValue();
+ method = *d;
+ }
+ }
+
+ if (method.coreIndex == -1) {
+ method.load(object->metaObject()->method(m_index));
+
+ if (method.coreIndex == -1)
+ return QV4::Value::undefinedValue();
+ }
+
+ if (method.isV4Function()) {
+ QV4::Value rv = QV4::Value::undefinedValue();
+
+ QQmlV4Function func(argc, args, &rv, m_qmlGlobal.value(),
+ QmlContextWrapper::getContext(m_qmlGlobal.value()),
+ v8Engine);
+ QQmlV4Function *funcptr = &func;
+
+ void *args[] = { 0, &funcptr };
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
+
+ return rv;
+ }
+
+ CallArgs callArgs(argc, args);
+ if (!method.isOverload()) {
+ return CallPrecise(object, method, v8Engine, callArgs);
+ } else {
+ return CallOverloaded(object, method, v8Engine, callArgs);
+ }
+}
+
+DEFINE_MANAGED_VTABLE(QObjectMethod);
+
+QmlSignalHandler::QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex)
+ : Object(engine)
+ , m_object(object)
+ , m_signalIndex(signalIndex)
+{
+ vtbl = &static_vtbl;
+ prototype = engine->objectPrototype;
+}
+
+DEFINE_MANAGED_VTABLE(QmlSignalHandler);
+
+void MultiplyWrappedQObjectMap::insert(QObject *key, Object *value)
+{
+ QHash<QObject*, Object*>::insert(key, value);
+ connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
+}
+
+MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it)
+{
+ disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
+ return QHash<QObject*, Object*>::erase(it);
+}
+
+void MultiplyWrappedQObjectMap::remove(QObject *key)
+{
+ Iterator it = find(key);
+ if (it == end())
+ return;
+ erase(it);
+}
+
+void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
+{
+ QHash<QObject*, Object*>::remove(object);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/qml/v4/qv4qobjectwrapper_p.h b/src/qml/qml/v4/qv4qobjectwrapper_p.h
new file mode 100644
index 0000000000..6580d19fe9
--- /dev/null
+++ b/src/qml/qml/v4/qv4qobjectwrapper_p.h
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4QOBJECTWRAPPER_P_H
+#define QV4QOBJECTWRAPPER_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/qglobal.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qhash.h>
+#include <private/qhashedstring_p.h>
+#include <private/qqmldata_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qintrusivelist_p.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4functionobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+class QQmlData;
+class QQmlPropertyCache;
+class QQmlPropertyData;
+
+namespace QV4 {
+struct QObjectSlotDispatcher;
+
+struct Q_QML_EXPORT QObjectWrapper : public QV4::Object
+{
+ Q_MANAGED
+
+ enum RevisionMode { IgnoreRevision, CheckRevision };
+
+ static void initializeBindings(ExecutionEngine *engine);
+
+ QObject *object() const { return m_object.data(); }
+
+ Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false);
+ static Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0);
+
+ static bool setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value);
+
+ static Value wrap(ExecutionEngine *engine, QObject *object);
+
+ using Object::get;
+
+private:
+ static Value create(ExecutionEngine *engine, QQmlData *ddata, QObject *object);
+
+ QObjectWrapper(ExecutionEngine *engine, QObject *object);
+
+ QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const;
+
+ QPointer<QObject> m_object;
+ String *m_destroy;
+ String *m_toString;
+
+ static Value get(Managed *m, String *name, bool *hasProperty);
+ static void put(Managed *m, String *name, const Value &value);
+ static PropertyAttributes query(const Managed *, String *name);
+ static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes);
+ static void markObjects(Managed *that);
+ static void collectDeletables(Managed *m, GCDeletable **deletable);
+ static void destroy(Managed *that)
+ {
+ static_cast<QObjectWrapper *>(that)->~QObjectWrapper();
+ }
+
+ static Value method_connect(SimpleCallContext *ctx);
+ static Value method_disconnect(SimpleCallContext *ctx);
+};
+
+struct QObjectMethod : public QV4::FunctionObject
+{
+ Q_MANAGED
+
+ enum { DestroyMethod = -1, ToStringMethod = -2 };
+
+ static Value create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Value::undefinedValue());
+
+ int methodIndex() const { return m_index; }
+ QObject *object() const { return m_object.data(); }
+
+private:
+ QObjectMethod(QV4::ExecutionContext *scope, QObject *object, int index, const QV4::Value &qmlGlobal);
+
+ QV4::Value method_toString(QV4::ExecutionContext *ctx);
+ QV4::Value method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc);
+
+ QPointer<QObject> m_object;
+ int m_index;
+ QV4::PersistentValue m_qmlGlobal;
+
+ static Value call(Managed *, const Value &thisObject, Value *args, int argc);
+
+ Value callInternal(const Value &, Value *args, int argc);
+
+ static void destroy(Managed *that)
+ {
+ static_cast<QObjectMethod *>(that)->~QObjectMethod();
+ }
+};
+
+struct QmlSignalHandler : public QV4::Object
+{
+ Q_MANAGED
+
+ QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex);
+
+ int signalIndex() const { return m_signalIndex; }
+ QObject *object() const { return m_object.data(); }
+
+private:
+ QPointer<QObject> m_object;
+ int m_signalIndex;
+
+ static void destroy(Managed *that)
+ {
+ static_cast<QmlSignalHandler *>(that)->~QmlSignalHandler();
+ }
+};
+
+class MultiplyWrappedQObjectMap : public QObject,
+ private QHash<QObject*, Object*>
+{
+ Q_OBJECT
+public:
+ typedef QHash<QObject*, Object*>::ConstIterator ConstIterator;
+ typedef QHash<QObject*, Object*>::Iterator Iterator;
+
+ ConstIterator begin() const { return QHash<QObject*, Object*>::constBegin(); }
+ Iterator begin() { return QHash<QObject*, Object*>::begin(); }
+ ConstIterator end() const { return QHash<QObject*, Object*>::constEnd(); }
+ Iterator end() { return QHash<QObject*, Object*>::end(); }
+
+ void insert(QObject *key, Object *value);
+ Object *value(QObject *key) const { return QHash<QObject*, Object*>::value(key, 0); }
+ Iterator erase(Iterator it);
+ void remove(QObject *key);
+
+private slots:
+ void removeDestroyedObject(QObject*);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4QOBJECTWRAPPER_P_H
+
+
diff --git a/src/qml/qml/v4/qv4regexp.cpp b/src/qml/qml/v4/qv4regexp.cpp
new file mode 100644
index 0000000000..ab01ce7650
--- /dev/null
+++ b/src/qml/qml/v4/qv4regexp.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4regexp_p.h"
+
+#include "qv4engine_p.h"
+
+using namespace QV4;
+
+RegExpCache::~RegExpCache()
+{
+ for (RegExpCache::Iterator it = begin(), e = end();
+ it != e; ++it)
+ it.value()->m_cache = 0;
+ clear();
+}
+
+DEFINE_MANAGED_VTABLE(RegExp);
+
+uint RegExp::match(const QString &string, int start, uint *matchOffsets)
+{
+ if (!isValid())
+ return JSC::Yarr::offsetNoMatch;
+
+ WTF::String s(string);
+
+#if ENABLE(YARR_JIT)
+ if (!m_jitCode.isFallBack() && m_jitCode.has16BitCode())
+ return m_jitCode.execute(s.characters16(), start, s.length(), (int*)matchOffsets).start;
+#endif
+
+ return JSC::Yarr::interpret(m_byteCode.get(), s.characters16(), string.length(), start, matchOffsets);
+}
+
+RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline)
+{
+ RegExpCacheKey key(pattern, ignoreCase, multiline);
+
+ RegExpCache *cache = engine->regExpCache;
+ if (cache) {
+ if (RegExp *result = cache->value(key))
+ return result;
+ }
+
+ RegExp *result = new (engine->memoryManager) RegExp(engine, pattern, ignoreCase, multiline);
+
+ if (!cache)
+ cache = engine->regExpCache = new RegExpCache;
+
+ result->m_cache = cache;
+ cache->insert(key, result);
+
+ return result;
+}
+
+RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline)
+ : Managed(engine->emptyClass)
+ , m_pattern(pattern)
+ , m_cache(0)
+ , m_subPatternCount(0)
+ , m_ignoreCase(ignoreCase)
+ , m_multiLine(multiline)
+{
+ vtbl = &static_vtbl;
+ type = Type_RegExpObject;
+
+ if (!engine)
+ return;
+ const char* error = 0;
+ JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error);
+ if (error)
+ return;
+ m_subPatternCount = yarrPattern.m_numSubpatterns;
+ m_byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator);
+#if ENABLE(YARR_JIT)
+ if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) {
+ JSC::JSGlobalData dummy(engine->regExpAllocator);
+ JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, m_jitCode);
+ }
+#endif
+}
+
+RegExp::~RegExp()
+{
+ if (m_cache) {
+ RegExpCacheKey key(this);
+ m_cache->remove(key);
+ }
+ _data = 0;
+}
+
+void RegExp::destroy(Managed *that)
+{
+ static_cast<RegExp*>(that)->~RegExp();
+}
+
+void RegExp::markObjects(Managed *that)
+{
+}
+
+Value RegExp::get(Managed *m, String *name, bool *hasProperty)
+{
+ return Value::undefinedValue();
+}
+
+Value RegExp::getIndexed(Managed *m, uint index, bool *hasProperty)
+{
+ return Value::undefinedValue();
+}
+
+void RegExp::put(Managed *m, String *name, const Value &value)
+{
+}
+
+void RegExp::putIndexed(Managed *m, uint index, const Value &value)
+{
+}
+
+PropertyAttributes RegExp::query(const Managed *m, String *name)
+{
+ return Attr_Invalid;
+}
+
+PropertyAttributes RegExp::queryIndexed(const Managed *m, uint index)
+{
+ return Attr_Invalid;
+}
+
+bool RegExp::deleteProperty(Managed *, String *)
+{
+ return false;
+}
+
+bool RegExp::deleteIndexedProperty(Managed *m, uint index)
+{
+ return false;
+}
+
+Property *RegExp::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes)
+{
+ return 0;
+}
diff --git a/src/qml/qml/v4/qv4regexp_p.h b/src/qml/qml/v4/qv4regexp_p.h
new file mode 100644
index 0000000000..6edbd4b2ad
--- /dev/null
+++ b/src/qml/qml/v4/qv4regexp_p.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4REGEXP_H
+#define QV4REGEXP_H
+
+#include <QString>
+#include <QVector>
+
+#include <wtf/RefPtr.h>
+#include <wtf/FastAllocBase.h>
+#include <wtf/BumpPointerAllocator.h>
+
+#include <limits.h>
+
+#include <yarr/Yarr.h>
+#include <yarr/YarrInterpreter.h>
+#include <yarr/YarrJIT.h>
+
+#include "qv4managed_p.h"
+#include "qv4engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ExecutionEngine;
+
+struct RegExpCacheKey
+{
+ RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine)
+ : pattern(pattern)
+ , ignoreCase(ignoreCase)
+ , multiLine(multiLine)
+ { }
+ explicit inline RegExpCacheKey(const RegExp *re);
+
+ bool operator==(const RegExpCacheKey &other) const
+ { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; }
+ bool operator!=(const RegExpCacheKey &other) const
+ { return !operator==(other); }
+
+ QString pattern;
+ uint ignoreCase : 1;
+ uint multiLine : 1;
+};
+
+inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW
+{ return qHash(key.pattern, seed); }
+
+class RegExpCache : public QHash<RegExpCacheKey, RegExp*>
+{
+public:
+ ~RegExpCache();
+};
+
+class RegExp : public Managed
+{
+ Q_MANAGED
+public:
+ static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false);
+ ~RegExp();
+
+ QString pattern() const { return m_pattern; }
+
+ bool isValid() const { return m_byteCode.get(); }
+
+ uint match(const QString& string, int start, uint *matchOffsets);
+
+ bool ignoreCase() const { return m_ignoreCase; }
+ bool multiLine() const { return m_multiLine; }
+ int captureCount() const { return m_subPatternCount + 1; }
+
+protected:
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+ static Value get(Managed *m, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, uint index, bool *hasProperty);
+ static void put(Managed *m, String *name, const Value &value);
+ static void putIndexed(Managed *m, uint index, const Value &value);
+ static PropertyAttributes query(const Managed *m, String *name);
+ static PropertyAttributes queryIndexed(const Managed *m, uint index);
+ static bool deleteProperty(Managed *, String *);
+ static bool deleteIndexedProperty(Managed *m, uint index);
+ static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes);
+
+private:
+ friend class RegExpCache;
+ Q_DISABLE_COPY(RegExp);
+ RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline);
+
+ const QString m_pattern;
+ OwnPtr<JSC::Yarr::BytecodePattern> m_byteCode;
+#if ENABLE(YARR_JIT)
+ JSC::Yarr::YarrCodeBlock m_jitCode;
+#endif
+ RegExpCache *m_cache;
+ int m_subPatternCount;
+ const bool m_ignoreCase;
+ const bool m_multiLine;
+};
+
+inline RegExpCacheKey::RegExpCacheKey(const RegExp *re)
+ : pattern(re->pattern())
+ , ignoreCase(re->ignoreCase())
+ , multiLine(re->multiLine())
+{}
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4REGEXP_H
diff --git a/src/qml/qml/v4/qv4regexpobject.cpp b/src/qml/qml/v4/qv4regexpobject.cpp
new file mode 100644
index 0000000000..0dc14e5722
--- /dev/null
+++ b/src/qml/qml/v4/qv4regexpobject.cpp
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4regexpobject_p.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4stringobject_p.h"
+#include "qv4mm_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/qmath.h>
+#include <QtCore/QDebug>
+#include <QtCore/qregexp.h>
+#include <cassert>
+#include <typeinfo>
+#include <iostream>
+#include "qv4alloca_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(RegExpObject);
+
+RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global)
+ : Object(engine)
+ , value(value)
+ , global(global)
+{
+ init(engine);
+}
+
+// Converts a QRegExp to a JS RegExp.
+// The conversion is not 100% exact since ECMA regexp and QRegExp
+// have different semantics/flags, but we try to do our best.
+RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re)
+ : Object(engine)
+ , value(0)
+ , global(false)
+{
+ // Convert the pattern to a ECMAScript pattern.
+ QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax());
+ if (re.isMinimal()) {
+ QString ecmaPattern;
+ int len = pattern.length();
+ ecmaPattern.reserve(len);
+ int i = 0;
+ const QChar *wc = pattern.unicode();
+ bool inBracket = false;
+ while (i < len) {
+ QChar c = wc[i++];
+ ecmaPattern += c;
+ switch (c.unicode()) {
+ case '?':
+ case '+':
+ case '*':
+ case '}':
+ if (!inBracket)
+ ecmaPattern += QLatin1Char('?');
+ break;
+ case '\\':
+ if (i < len)
+ ecmaPattern += wc[i++];
+ break;
+ case '[':
+ inBracket = true;
+ break;
+ case ']':
+ inBracket = false;
+ break;
+ default:
+ break;
+ }
+ }
+ pattern = ecmaPattern;
+ }
+
+ value = RegExp::create(engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false);
+
+ init(engine);
+}
+
+void RegExpObject::init(ExecutionEngine *engine)
+{
+ vtbl = &static_vtbl;
+ type = Type_RegExpObject;
+
+ Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")),
+ Attr_NotEnumerable|Attr_NotConfigurable);
+ lastIndexProperty->value = Value::fromInt32(0);
+ if (!this->value)
+ return;
+
+ QString p = this->value->pattern();
+ if (p.isEmpty()) {
+ p = QStringLiteral("(?:)");
+ } else {
+ // escape certain parts, see ch. 15.10.4
+ p.replace('/', QLatin1String("\\/"));
+ }
+
+ defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(p)));
+ defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global));
+ defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase()));
+ defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine()));
+}
+
+
+void RegExpObject::destroy(Managed *that)
+{
+ static_cast<RegExpObject *>(that)->~RegExpObject();
+}
+
+void RegExpObject::markObjects(Managed *that)
+{
+ RegExpObject *re = static_cast<RegExpObject*>(that);
+ if (re->value)
+ re->value->mark();
+ Object::markObjects(that);
+}
+
+Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx)
+{
+ assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex"))));
+ return &memberData[0];
+}
+
+// Converts a JS RegExp to a QRegExp.
+// The conversion is not 100% exact since ECMA regexp and QRegExp
+// have different semantics/flags, but we try to do our best.
+QRegExp RegExpObject::toQRegExp() const
+{
+ Qt::CaseSensitivity caseSensitivity = value->ignoreCase() ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ return QRegExp(value->pattern(), caseSensitivity, QRegExp::RegExp2);
+}
+
+QString RegExpObject::toString() const
+{
+ QString result = QChar('/') + source();
+ result += QChar('/');
+ if (global)
+ result += QChar('g');
+ if (value->ignoreCase())
+ result += QChar('i');
+ if (value->multiLine())
+ result += QChar('m');
+ return result;
+}
+
+QString RegExpObject::source() const
+{
+ return const_cast<RegExpObject *>(this)->get(internalClass->engine->newIdentifier(QStringLiteral("source"))).stringValue()->toQString();
+}
+
+uint RegExpObject::flags() const
+{
+ uint f = 0;
+ if (global)
+ f |= QV4::RegExpObject::RegExp_Global;
+ if (value->ignoreCase())
+ f |= QV4::RegExpObject::RegExp_IgnoreCase;
+ if (value->multiLine())
+ f |= QV4::RegExpObject::RegExp_Multiline;
+ return f;
+}
+
+DEFINE_MANAGED_VTABLE(RegExpCtor);
+
+RegExpCtor::RegExpCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("RegExp")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value RegExpCtor::construct(Managed *m, Value *argv, int argc)
+{
+ Value r = argc > 0 ? argv[0] : Value::undefinedValue();
+ Value f = argc > 1 ? argv[1] : Value::undefinedValue();
+ ExecutionContext *ctx = m->engine()->current;
+ if (RegExpObject *re = r.as<RegExpObject>()) {
+ if (!f.isUndefined())
+ ctx->throwTypeError();
+
+ RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global);
+ return Value::fromObject(o);
+ }
+
+ QString pattern;
+ if (!r.isUndefined())
+ pattern = r.toString(ctx)->toQString();
+
+ bool global = false;
+ bool ignoreCase = false;
+ bool multiLine = false;
+ if (!f.isUndefined()) {
+ f = __qmljs_to_string(f, ctx);
+ QString str = f.stringValue()->toQString();
+ for (int i = 0; i < str.length(); ++i) {
+ if (str.at(i) == QChar('g') && !global) {
+ global = true;
+ } else if (str.at(i) == QChar('i') && !ignoreCase) {
+ ignoreCase = true;
+ } else if (str.at(i) == QChar('m') && !multiLine) {
+ multiLine = true;
+ } else {
+ ctx->throwSyntaxError(0);
+ }
+ }
+ }
+
+ RegExp* re = RegExp::create(ctx->engine, pattern, ignoreCase, multiLine);
+ if (!re->isValid())
+ ctx->throwSyntaxError(0);
+
+ RegExpObject *o = ctx->engine->newRegExpObject(re, global);
+ return Value::fromObject(o);
+}
+
+Value RegExpCtor::call(Managed *that, const Value &, Value *argv, int argc)
+{
+ if (argc > 0 && argv[0].as<RegExpObject>()) {
+ if (argc == 1 || argv[1].isUndefined())
+ return argv[0];
+ }
+
+ return construct(that, argv, argc);
+}
+
+void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2));
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1);
+ defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2);
+}
+
+Value RegExpPrototype::method_exec(SimpleCallContext *ctx)
+{
+ RegExpObject *r = ctx->thisObject.as<RegExpObject>();
+ if (!r)
+ ctx->throwTypeError();
+
+ Value arg = ctx->argument(0);
+ arg = __qmljs_to_string(arg, ctx);
+ QString s = arg.stringValue()->toQString();
+
+ int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0;
+ if (offset < 0 || offset > s.length()) {
+ r->lastIndexProperty(ctx)->value = Value::fromInt32(0);
+ return Value::nullValue();
+ }
+
+ uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint));
+ int result = r->value->match(s, offset, matchOffsets);
+ if (result == -1) {
+ r->lastIndexProperty(ctx)->value = Value::fromInt32(0);
+ return Value::nullValue();
+ }
+
+ // fill in result data
+ ArrayObject *array = ctx->engine->newArrayObject();
+ for (int i = 0; i < r->value->captureCount(); ++i) {
+ int start = matchOffsets[i * 2];
+ int end = matchOffsets[i * 2 + 1];
+ Value entry = Value::undefinedValue();
+ if (start != -1 && end != -1)
+ entry = Value::fromString(ctx, s.mid(start, end - start));
+ array->push_back(entry);
+ }
+
+ array->put(ctx, QLatin1String("index"), Value::fromInt32(result));
+ array->put(ctx, QLatin1String("input"), arg);
+
+ if (r->global)
+ r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]);
+
+ return Value::fromObject(array);
+}
+
+Value RegExpPrototype::method_test(SimpleCallContext *ctx)
+{
+ Value r = method_exec(ctx);
+ return Value::fromBoolean(!r.isNull());
+}
+
+Value RegExpPrototype::method_toString(SimpleCallContext *ctx)
+{
+ RegExpObject *r = ctx->thisObject.as<RegExpObject>();
+ if (!r)
+ ctx->throwTypeError();
+
+ return Value::fromString(ctx, r->toString());
+}
+
+Value RegExpPrototype::method_compile(SimpleCallContext *ctx)
+{
+ RegExpObject *r = ctx->thisObject.as<RegExpObject>();
+ if (!r)
+ ctx->throwTypeError();
+
+ RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx->arguments, ctx->argumentCount).as<RegExpObject>();
+
+ r->value = re->value;
+ r->global = re->global;
+ return Value::undefinedValue();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4regexpobject_p.h b/src/qml/qml/v4/qv4regexpobject_p.h
new file mode 100644
index 0000000000..0b9b7122a9
--- /dev/null
+++ b/src/qml/qml/v4/qv4regexpobject_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4REGEXPOBJECT_H
+#define QV4REGEXPOBJECT_H
+
+#include "qv4runtime_p.h"
+#include "qv4engine_p.h"
+#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"
+#include "qv4regexp_p.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct RegExp;
+
+struct RegExpObject: Object {
+ Q_MANAGED
+ // needs to be compatible with the flags in qv4jsir_p.h
+ enum Flags {
+ RegExp_Global = 0x01,
+ RegExp_IgnoreCase = 0x02,
+ RegExp_Multiline = 0x04
+ };
+
+ RegExp* value;
+ Property *lastIndexProperty(ExecutionContext *ctx);
+ bool global;
+ RegExpObject(ExecutionEngine *engine, RegExp* value, bool global);
+ RegExpObject(ExecutionEngine *engine, const QRegExp &re);
+ ~RegExpObject() {}
+
+ void init(ExecutionEngine *engine);
+
+ QRegExp toQRegExp() const;
+ QString toString() const;
+ QString source() const;
+ uint flags() const;
+
+protected:
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+};
+
+
+struct RegExpCtor: FunctionObject
+{
+ RegExpCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct RegExpPrototype: RegExpObject
+{
+ RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(engine, QString()), false) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_exec(SimpleCallContext *ctx);
+ static Value method_test(SimpleCallContext *ctx);
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_compile(SimpleCallContext *ctx);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4/qv4runtime.cpp b/src/qml/qml/v4/qv4runtime.cpp
new file mode 100644
index 0000000000..deccb3d4ea
--- /dev/null
+++ b/src/qml/qml/v4/qv4runtime.cpp
@@ -0,0 +1,1269 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4global_p.h"
+#include "qv4runtime_p.h"
+#include "qv4object_p.h"
+#include "qv4jsir_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4globalobject_p.h"
+#include "qv4stringobject_p.h"
+#include "qv4lookup_p.h"
+#include "qv4function_p.h"
+#include "qv4exception_p.h"
+#include "private/qlocale_tools_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/QDebug>
+#include <cstdio>
+#include <cassert>
+#include <typeinfo>
+#include <stdlib.h>
+
+#include "../../../3rdparty/double-conversion/double-conversion.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+void __qmljs_numberToString(QString *result, double num, int radix)
+{
+ Q_ASSERT(result);
+
+ if (std::isnan(num)) {
+ *result = QStringLiteral("NaN");
+ return;
+ } else if (qIsInf(num)) {
+ *result = QLatin1String(num < 0 ? "-Infinity" : "Infinity");
+ return;
+ }
+
+ if (radix == 10) {
+ char str[100];
+ double_conversion::StringBuilder builder(str, sizeof(str));
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder);
+ *result = QString::fromLatin1(builder.Finalize());
+ return;
+ }
+
+ result->clear();
+ bool negative = false;
+
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+
+ double frac = num - ::floor(num);
+ num = Value::toInteger(num);
+
+ do {
+ char c = (char)::fmod(num, radix);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ result->prepend(QLatin1Char(c));
+ num = ::floor(num / radix);
+ } while (num != 0);
+
+ if (frac != 0) {
+ result->append(QLatin1Char('.'));
+ do {
+ frac = frac * radix;
+ char c = (char)::floor(frac);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ result->append(QLatin1Char(c));
+ frac = frac - ::floor(frac);
+ } while (frac != 0);
+ }
+
+ if (negative)
+ result->prepend(QLatin1Char('-'));
+}
+
+void __qmljs_init_closure(ExecutionContext *ctx, Value *result, Function *clos)
+{
+ assert(clos);
+ *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos));
+}
+
+Function *__qmljs_register_function(ExecutionContext *ctx, String *name,
+ bool hasDirectEval,
+ bool usesArgumentsObject, bool isStrict,
+ bool hasNestedFunctions,
+ String **formals, unsigned formalCount,
+ String **locals, unsigned localCount)
+{
+ Function *f = ctx->engine->newFunction(name ? name->toQString() : QString());
+
+ f->hasDirectEval = hasDirectEval;
+ f->usesArgumentsObject = usesArgumentsObject;
+ f->isStrict = isStrict;
+ f->hasNestedFunctions = hasNestedFunctions;
+
+ for (unsigned i = 0; i < formalCount; ++i)
+ if (formals[i])
+ f->formals.append(formals[i]);
+ for (unsigned i = 0; i < localCount; ++i)
+ if (locals[i])
+ f->locals.append(locals[i]);
+
+ return f;
+}
+
+void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index)
+{
+ if (Object *o = base.asObject()) {
+ uint n = index.asArrayIndex();
+ if (n < UINT_MAX) {
+ Value res = Value::fromBoolean(o->deleteIndexedProperty(n));
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+
+ String *name = index.toString(ctx);
+ __qmljs_delete_member(ctx, result, base, name);
+}
+
+void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name)
+{
+ Object *obj = base.toObject(ctx);
+ Value res = Value::fromBoolean(obj->deleteProperty(name));
+ if (result)
+ *result = res;
+}
+
+void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name)
+{
+ Value res = Value::fromBoolean(ctx->deleteProperty(name));
+ if (result)
+ *result = res;
+}
+
+void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ Value pleft = __qmljs_to_primitive(left, PREFERREDTYPE_HINT);
+ Value pright = __qmljs_to_primitive(right, PREFERREDTYPE_HINT);
+ if (pleft.isString() || pright.isString()) {
+ if (!pleft.isString())
+ pleft = __qmljs_to_string(pleft, ctx);
+ if (!pright.isString())
+ pright = __qmljs_to_string(pright, ctx);
+ String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue());
+ *result = Value::fromString(string);
+ return;
+ }
+ double x = __qmljs_to_number(pleft);
+ double y = __qmljs_to_number(pright);
+ *result = Value::fromDouble(x + y);
+}
+
+void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ Object *o = right.asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ bool r = o->hasInstance(left);
+ *result = Value::fromBoolean(r);
+}
+
+void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ if (!right.isObject())
+ ctx->throwTypeError();
+ String *s = left.toString(ctx);
+ bool r = right.objectValue()->__hasProperty__(s);
+ *result = Value::fromBoolean(r);
+}
+
+void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_bit_and);
+}
+
+void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_bit_or);
+}
+
+void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_bit_xor);
+}
+
+void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_add);
+}
+
+void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_sub);
+}
+
+void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_mul);
+}
+
+void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_div);
+}
+
+void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_mod);
+}
+
+void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_shl);
+}
+
+void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_shr);
+}
+
+void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->inplaceBitOp(name, value, __qmljs_ushr);
+}
+
+void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs);
+}
+
+void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs);
+}
+
+void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs);
+}
+
+void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_add, index, rhs);
+}
+
+void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs);
+}
+
+void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs);
+}
+
+void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_div, index, rhs);
+}
+
+void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs);
+}
+
+void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs);
+}
+
+void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs);
+}
+
+void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs)
+{
+ Object *obj = base.toObject(ctx);
+ obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs);
+}
+
+void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs);
+}
+
+void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs);
+}
+
+void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs);
+}
+
+void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_add, name, rhs);
+}
+
+void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_sub, name, rhs);
+}
+
+void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_mul, name, rhs);
+}
+
+void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_div, name, rhs);
+}
+
+void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_mod, name, rhs);
+}
+
+void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_shl, name, rhs);
+}
+
+void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_shr, name, rhs);
+}
+
+void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs)
+{
+ Object *o = base.toObject(ctx);
+ o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs);
+}
+
+double __qmljs_string_to_number(const QString &string)
+{
+ QString s = string.trimmed();
+ if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X")))
+ return s.toLong(0, 16);
+ bool ok;
+ QByteArray ba = s.toLatin1();
+ const char *begin = ba.constData();
+ const char *end = 0;
+ double d = qstrtod(begin, &end, &ok);
+ if (end - begin != ba.size()) {
+ if (ba == "Infinity" || ba == "+Infinity")
+ d = Q_INFINITY;
+ else if (ba == "-Infinity")
+ d = -Q_INFINITY;
+ else
+ d = std::numeric_limits<double>::quiet_NaN();
+ }
+ return d;
+}
+
+Value __qmljs_string_from_number(ExecutionContext *ctx, double number)
+{
+ QString qstr;
+ __qmljs_numberToString(&qstr, number, 10);
+ String *string = ctx->engine->newString(qstr);
+ return Value::fromString(string);
+}
+
+String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second)
+{
+ const QString &a = first->toQString();
+ const QString &b = second->toQString();
+ QString newStr(a.length() + b.length(), Qt::Uninitialized);
+ QChar *data = newStr.data();
+ memcpy(data, a.constData(), a.length()*sizeof(QChar));
+ data += a.length();
+ memcpy(data, b.constData(), b.length()*sizeof(QChar));
+
+ return ctx->engine->newString(newStr);
+}
+
+Value __qmljs_object_default_value(Object *object, int typeHint)
+{
+ if (typeHint == PREFERREDTYPE_HINT) {
+ if (object->asDateObject())
+ typeHint = STRING_HINT;
+ else
+ typeHint = NUMBER_HINT;
+ }
+
+ ExecutionEngine *engine = object->internalClass->engine;
+ String *meth1 = engine->newString("toString");
+ String *meth2 = engine->newString("valueOf");
+
+ if (typeHint == NUMBER_HINT)
+ qSwap(meth1, meth2);
+
+ ExecutionContext *ctx = engine->current;
+
+ Value conv = object->get(meth1);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(Value::fromObject(object), 0, 0);
+ if (r.isPrimitive())
+ return r;
+ }
+
+ conv = object->get(meth2);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(Value::fromObject(object), 0, 0);
+ if (r.isPrimitive())
+ return r;
+ }
+
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Bool __qmljs_to_boolean(const Value &value)
+{
+ return value.toBoolean();
+}
+
+
+Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value)
+{
+ assert(!value.isObject());
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ case Value::Null_Type:
+ ctx->throwTypeError();
+ case Value::Boolean_Type:
+ return ctx->engine->newBooleanObject(value);
+ case Value::String_Type:
+ return ctx->engine->newStringObject(value);
+ break;
+ case Value::Object_Type:
+ Q_UNREACHABLE();
+ case Value::Integer_Type:
+ default: // double
+ return ctx->engine->newNumberObject(value);
+ }
+}
+
+String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ return ctx->engine->id_undefined;
+ case Value::Null_Type:
+ return ctx->engine->id_null;
+ case Value::Boolean_Type:
+ if (value.booleanValue())
+ return ctx->engine->id_true;
+ else
+ return ctx->engine->id_false;
+ case Value::String_Type:
+ return value.stringValue();
+ case Value::Object_Type: {
+ Value prim = __qmljs_to_primitive(value, STRING_HINT);
+ if (prim.isPrimitive())
+ return __qmljs_convert_to_string(ctx, prim);
+ else
+ ctx->throwTypeError();
+ }
+ case Value::Integer_Type:
+ return __qmljs_string_from_number(ctx, value.int_32).stringValue();
+ default: // double
+ return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue();
+ } // switch
+}
+
+void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value)
+{
+ Object *o = object.toObject(ctx);
+ o->put(name, value);
+}
+
+void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index)
+{
+ uint idx = index.asArrayIndex();
+
+ Object *o = object.asObject();
+ if (!o) {
+ if (idx < UINT_MAX) {
+ if (String *str = object.asString()) {
+ if (idx >= (uint)str->toQString().length()) {
+ if (result)
+ *result = Value::undefinedValue();
+ return;
+ }
+ const QString s = str->toQString().mid(idx, 1);
+ if (result)
+ *result = Value::fromString(ctx, s);
+ return;
+ }
+ }
+
+ if (object.isNull() || object.isUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQString()).arg(object.toQString());
+ ctx->throwTypeError(message);
+ }
+
+ o = __qmljs_convert_to_object(ctx, object);
+ }
+
+ if (idx < UINT_MAX) {
+ uint pidx = o->propertyIndexFromArrayIndex(idx);
+ if (pidx < UINT_MAX) {
+ if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) {
+ if (result)
+ *result = o->arrayData[pidx].value;
+ return;
+ }
+ }
+
+ Value res = o->getIndexed(idx);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ String *name = index.toString(ctx);
+ Value res = o->get(name);
+ if (result)
+ *result = res;
+}
+
+void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value)
+{
+ Object *o = object.toObject(ctx);
+
+ uint idx = index.asArrayIndex();
+ if (idx < UINT_MAX) {
+ uint pidx = o->propertyIndexFromArrayIndex(idx);
+ if (pidx < UINT_MAX) {
+ if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) {
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+ return;
+ }
+
+ Property *p = o->arrayData + pidx;
+ if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) {
+ p->value = value;
+ return;
+ }
+
+ if (o->arrayAttributes[pidx].isAccessor()) {
+ FunctionObject *setter = p->setter();
+ if (!setter) {
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+ return;
+ }
+
+ Value args[1];
+ args[0] = value;
+ setter->call(Value::fromObject(o), args, 1);
+ return;
+ }
+ }
+ o->putIndexed(idx, value);
+ return;
+ }
+
+ String *name = index.toString(ctx);
+ o->put(name, value);
+}
+
+void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in)
+{
+ Object *o = 0;
+ if (!in.isNull() && !in.isUndefined())
+ o = in.toObject(ctx);
+ Object *it = ctx->engine->newForEachIteratorObject(ctx, o);
+ *result = Value::fromObject(it);
+}
+
+void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator)
+{
+ assert(foreach_iterator.isObject());
+
+ ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue());
+ assert(it->as<ForEachIteratorObject>());
+
+ *result = it->nextPropertyName();
+}
+
+
+void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value)
+{
+ ctx->setProperty(name, value);
+}
+
+void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name)
+{
+ Value res;
+ Managed *m = object.asManaged();
+ if (m) {
+ res = m->get(name);
+ } else {
+ if (object.isNull() || object.isUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()).arg(object.toQString());
+ ctx->throwTypeError(message);
+ }
+
+ m = __qmljs_convert_to_object(ctx, object);
+ res = m->get(name);
+ }
+ if (result)
+ *result = res;
+}
+
+void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name)
+{
+ *result = ctx->getProperty(name);
+}
+
+uint __qmljs_equal(const Value &x, const Value &y)
+{
+ if (x.type() == y.type()) {
+ switch (x.type()) {
+ case Value::Undefined_Type:
+ return true;
+ case Value::Null_Type:
+ return true;
+ case Value::Boolean_Type:
+ return x.booleanValue() == y.booleanValue();
+ break;
+ case Value::Integer_Type:
+ return x.integerValue() == y.integerValue();
+ case Value::String_Type:
+ return x.stringValue()->isEqualTo(y.stringValue());
+ case Value::Object_Type:
+ if (x.objectValue() == y.objectValue())
+ return true;
+ return x.objectValue()->isEqualTo(y.objectValue());
+ default: // double
+ return x.doubleValue() == y.doubleValue();
+ }
+ // unreachable
+ } else {
+ if (x.isNumber() && y.isNumber())
+ return x.asDouble() == y.asDouble();
+ if (x.isNull() && y.isUndefined()) {
+ return true;
+ } else if (x.isUndefined() && y.isNull()) {
+ return true;
+ } else if (x.isNumber() && y.isString()) {
+ Value ny = Value::fromDouble(__qmljs_to_number(y));
+ return __qmljs_equal(x, ny);
+ } else if (x.isString() && y.isNumber()) {
+ Value nx = Value::fromDouble(__qmljs_to_number(x));
+ return __qmljs_equal(nx, y);
+ } else if (x.isBoolean()) {
+ Value nx = Value::fromDouble((double) x.booleanValue());
+ return __qmljs_equal(nx, y);
+ } else if (y.isBoolean()) {
+ Value ny = Value::fromDouble((double) y.booleanValue());
+ return __qmljs_equal(x, ny);
+ } else if ((x.isNumber() || x.isString()) && y.isObject()) {
+ Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT);
+ return __qmljs_equal(x, py);
+ } else if (x.isObject() && (y.isNumber() || y.isString())) {
+ Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT);
+ return __qmljs_equal(px, y);
+ }
+ }
+
+ return false;
+}
+
+Bool __qmljs_strict_equal(const Value &x, const Value &y)
+{
+ TRACE2(x, y);
+
+ if (x.rawValue() == y.rawValue()) {
+ if (x.isDouble())
+ return !std::isnan(x.doubleValue());
+ return true;
+ }
+ if (x.isNumber() && y.isNumber())
+ return x.asDouble() == y.asDouble();
+ if (x.isString() && y.isString())
+ return x.stringValue()->isEqualTo(y.stringValue());
+ return false;
+}
+
+
+void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc)
+{
+ Lookup *l = context->lookups + index;
+ Value v;
+ l->globalGetter(l, context, &v);
+ FunctionObject *o = v.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value thisObject = Value::undefinedValue();
+
+ if (o == context->engine->evalFunction && l->name->isEqualTo(context->engine->id_eval)) {
+ Value res = static_cast<EvalFunction *>(o)->evalCall(thisObject, args, argc, true);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ Value res = o->call(thisObject, args, argc);
+ if (result)
+ *result = res;
+}
+
+
+void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ Object *base;
+ Value func = context->getPropertyAndBase(name, &base);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o) {
+ QString objectAsString = QStringLiteral("[null]");
+ if (base)
+ objectAsString = Value::fromObject(base).toQString();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString);
+ context->throwTypeError(msg);
+ }
+
+ Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue();
+
+ if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) {
+ Value res = static_cast<EvalFunction *>(o)->evalCall(thisObject, args, argc, true);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ Value res = o->call(thisObject, args, argc);
+ if (result)
+ *result = res;
+}
+
+void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc)
+{
+ Value thisObject = thatObject;
+ Managed *baseObject = thisObject.asManaged();
+ if (!baseObject) {
+ if (thisObject.isNull() || thisObject.isUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(thisObject.toQString());
+ context->throwTypeError(message);
+ }
+
+ baseObject = __qmljs_convert_to_object(context, thisObject);
+ thisObject = Value::fromObject(static_cast<Object *>(baseObject));
+ }
+
+ Value func = baseObject->get(name);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o) {
+ QString error = QString("Property '%1' of object %2 is not a function").arg(name->toQString(), thisObject.toQString());
+ context->throwTypeError(error);
+ }
+
+ Value res = o->call(thisObject, args, argc);
+ if (result)
+ *result = res;
+}
+
+void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc)
+{
+ Lookup *l = context->lookups + index;
+
+ Object *baseObject;
+ if (thisObject.isObject())
+ baseObject = thisObject.objectValue();
+ else if (thisObject.isString())
+ baseObject = context->engine->stringPrototype;
+ else
+ baseObject = __qmljs_convert_to_object(context, thisObject);
+
+ PropertyAttributes attrs;
+ Property *p = l->lookup(baseObject, &attrs);
+ if (!p)
+ context->throwTypeError();
+ Value func = attrs.isData() ? p->value : baseObject->getValue(p, attrs);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value res = o->call(thisObject, args, argc);
+ if (result)
+ *result = res;
+}
+
+void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc)
+{
+ Object *baseObject = that.toObject(context);
+ Value thisObject = Value::fromObject(baseObject);
+
+ Value func = baseObject->get(index.toString(context));
+ Object *o = func.asObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value res = o->call(thisObject, args, argc);
+ if (result)
+ *result = res;
+}
+
+void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc)
+{
+ Object *o = func.asObject();
+ if (!o)
+ context->throwTypeError();
+ Value res = o->call(thisObject ? *thisObject : Value::undefinedValue(), args, argc);
+ if (result)
+ *result = res;
+}
+
+
+void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc)
+{
+ Lookup *l = context->lookups + index;
+ Value func;
+ l->globalGetter(l, context, &func);
+
+ if (Object *f = func.asObject()) {
+ Value res = f->construct(args, argc);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ context->throwTypeError();
+}
+
+
+void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ Value func = context->getProperty(name);
+ __qmljs_construct_value(context, result, func, args, argc);
+}
+
+void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc)
+{
+ if (Object *f = func.asObject()) {
+ Value res = f->construct(args, argc);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ context->throwTypeError();
+}
+
+void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc)
+{
+ Object *thisObject = base.toObject(context);
+
+ Value func = thisObject->get(name);
+ if (Object *f = func.asObject()) {
+ Value res = f->construct(args, argc);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ context->throwTypeError();
+}
+
+void __qmljs_throw(ExecutionContext *context, const Value &value)
+{
+ Exception::throwException(context, value);
+}
+
+void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value)
+{
+ if (!result)
+ return;
+ String *res = 0;
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ res = ctx->engine->id_undefined;
+ break;
+ case Value::Null_Type:
+ res = ctx->engine->id_object;
+ break;
+ case Value::Boolean_Type:
+ res = ctx->engine->id_boolean;
+ break;
+ case Value::String_Type:
+ res = ctx->engine->id_string;
+ break;
+ case Value::Object_Type:
+ if (value.objectValue()->asFunctionObject())
+ res = ctx->engine->id_function;
+ else
+ res = ctx->engine->id_object; // ### implementation-defined
+ break;
+ default:
+ res = ctx->engine->id_number;
+ break;
+ }
+ *result = Value::fromString(res);
+}
+
+void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name)
+{
+ Value res;
+ __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name));
+ if (result)
+ *result = res;
+}
+
+void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name)
+{
+ Object *obj = base.toObject(context);
+ Value res;
+ __qmljs_builtin_typeof(context, &res, obj->get(name));
+ if (result)
+ *result = res;
+}
+
+void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index)
+{
+ String *name = index.toString(context);
+ Object *obj = base.toObject(context);
+ Value res;
+ __qmljs_builtin_typeof(context, &res, obj->get(name));
+ if (result)
+ *result = res;
+}
+
+void __qmljs_builtin_post_increment(Value *result, Value *val)
+{
+ if (val->isInteger() && val->integerValue() < INT_MAX) {
+ if (result)
+ *result = *val;
+ val->int_32 += 1;
+ return;
+ }
+
+ double d = __qmljs_to_number(*val);
+ *val = Value::fromDouble(d + 1);
+ if (result)
+ *result = Value::fromDouble(d);
+}
+
+void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name)
+{
+ Value v = context->getProperty(name);
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ if (result)
+ *result = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ context->setProperty(name, v);
+}
+
+void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name)
+{
+ Object *o = base.toObject(context);
+
+ Value v = o->get(name);
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ if (result)
+ *result = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ o->put(name, v);
+}
+
+void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index)
+{
+ Object *o = base.toObject(context);
+
+ uint idx = index->asArrayIndex();
+
+ if (idx == UINT_MAX) {
+ String *s = index->toString(context);
+ return __qmljs_builtin_post_increment_member(context, result, base, s);
+ }
+
+ Value v = o->getIndexed(idx);
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ if (result)
+ *result = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ o->putIndexed(idx, v);
+}
+
+void __qmljs_builtin_post_decrement(Value *result, Value *val)
+{
+ if (val->isInteger() && val->integerValue() > INT_MIN) {
+ if (result)
+ *result = *val;
+ val->int_32 -= 1;
+ return;
+ }
+
+ double d = __qmljs_to_number(*val);
+ *val = Value::fromDouble(d - 1);
+ if (result)
+ *result = Value::fromDouble(d);
+}
+
+void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name)
+{
+ Value v = context->getProperty(name);
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ if (result)
+ *result = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ context->setProperty(name, v);
+}
+
+void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name)
+{
+ Object *o = base.toObject(context);
+
+ Value v = o->get(name);
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ if (result)
+ *result = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ o->put(name, v);
+}
+
+void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index)
+{
+ Object *o = base.toObject(context);
+
+ uint idx = index.asArrayIndex();
+
+ if (idx == UINT_MAX) {
+ String *s = index.toString(context);
+ return __qmljs_builtin_post_decrement_member(context, result, base, s);
+ }
+
+ Value v = o->getIndexed(idx);
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ if (result)
+ *result = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v);
+ if (result)
+ *result = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ o->putIndexed(idx, v);
+}
+
+ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx)
+{
+ Object *obj = o.toObject(ctx);
+ return ctx->engine->newWithContext(obj);
+}
+
+ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const Value &exceptionValue, ExecutionContext *ctx)
+{
+ return ctx->engine->newCatchContext(exceptionVarName, exceptionValue);
+}
+
+ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx)
+{
+ return ctx->engine->popContext();
+}
+
+void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name)
+{
+ ctx->createMutableBinding(name, deletable);
+}
+
+void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val)
+{
+ Object *o = object.asObject();
+ assert(o);
+
+ uint idx = name->asArrayIndex();
+ Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data);
+ pd->value = val ? *val : Value::undefinedValue();
+}
+
+void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length)
+{
+ ArrayObject *a = ctx->engine->newArrayObject();
+
+ // ### FIXME: We need to allocate the array data to avoid crashes other places
+ // This should rather be done when required
+ a->arrayReserve(length);
+ if (length) {
+ a->arrayDataLen = length;
+ Property *pd = a->arrayData;
+ for (uint i = 0; i < length; ++i) {
+ if (values[i].isEmpty()) {
+ a->ensureArrayAttributes();
+ pd->value = Value::undefinedValue();
+ a->arrayAttributes[i].clear();
+ } else {
+ pd->value = values[i];
+ }
+ ++pd;
+ }
+ a->setArrayLengthUnchecked(length);
+ }
+ *array = Value::fromObject(a);
+}
+
+void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter)
+{
+ Object *o = object.asObject();
+ assert(o);
+
+ uint idx = name->asArrayIndex();
+ Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor);
+ pd->setGetter(getter ? getter->asFunctionObject() : 0);
+ pd->setSetter(setter ? setter->asFunctionObject() : 0);
+}
+
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass)
+{
+ Object *o = ctx->engine->newObject(klass);
+
+ for (int i = 0; i < klass->size; ++i) {
+ if (klass->propertyData[i].isData())
+ o->memberData[i].value = *args++;
+ else {
+ o->memberData[i].setGetter(args->asFunctionObject());
+ args++;
+ o->memberData[i].setSetter(args->asFunctionObject());
+ args++;
+ }
+ }
+
+ *result = Value::fromObject(o);
+}
+
+void __qmljs_increment(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ if (value.isInteger())
+ *result = Value::fromInt32(value.integerValue() + 1);
+ else {
+ double d = __qmljs_to_number(value);
+ *result = Value::fromDouble(d + 1);
+ }
+}
+
+void __qmljs_decrement(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ if (value.isInteger())
+ *result = Value::fromInt32(value.integerValue() - 1);
+ else {
+ double d = __qmljs_to_number(value);
+ *result = Value::fromDouble(d - 1);
+ }
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4runtime_p.h b/src/qml/qml/v4/qv4runtime_p.h
new file mode 100644
index 0000000000..e6e6e0f3a0
--- /dev/null
+++ b/src/qml/qml/v4/qv4runtime_p.h
@@ -0,0 +1,717 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_RUNTIME_H
+#define QMLJS_RUNTIME_H
+
+#include "qv4global_p.h"
+#include "qv4value_p.h"
+#include "qv4math_p.h"
+
+
+#include <QtCore/QString>
+#include <QtCore/qnumeric.h>
+#include <QtCore/QDebug>
+#include <QtCore/qurl.h>
+
+#include <cmath>
+#include <cassert>
+#include <limits>
+
+//#include <wtf/MathExtras.h>
+
+#ifdef DO_TRACE_INSTR
+# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__);
+# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__);
+#else
+# define TRACE1(x)
+# define TRACE2(x, y)
+#endif // TRACE1
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+enum TypeHint {
+ PREFERREDTYPE_HINT,
+ NUMBER_HINT,
+ STRING_HINT
+};
+
+struct Function;
+struct Object;
+struct String;
+struct ExecutionContext;
+struct FunctionObject;
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct DateObject;
+struct RegExpObject;
+struct ArrayObject;
+struct ErrorObject;
+struct ExecutionEngine;
+struct InternalClass;
+
+// context
+void __qmljs_call_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc);
+void __qmljs_call_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, QV4::String *name, QV4::Value *args, int argc);
+void __qmljs_call_property_lookup(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &thisObject, uint index, QV4::Value *args, int argc);
+void __qmljs_call_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, const QV4::Value &index, QV4::Value *args, int argc);
+void __qmljs_call_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value *thisObject, const QV4::Value &func, QV4::Value *args, int argc);
+
+void __qmljs_construct_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc);
+void __qmljs_construct_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name, QV4::Value *args, int argc);
+void __qmljs_construct_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &func, QV4::Value *args, int argc);
+
+void __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &val);
+void __qmljs_builtin_typeof_name(QV4::ExecutionContext *context, QV4::Value* result, QV4::String *name);
+void __qmljs_builtin_typeof_member(QV4::ExecutionContext* context, QV4::Value* result, const QV4::Value &base, QV4::String *name);
+void __qmljs_builtin_typeof_element(QV4::ExecutionContext* context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index);
+
+void __qmljs_builtin_post_increment(QV4::Value *result, QV4::Value *val);
+void __qmljs_builtin_post_increment_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name);
+void __qmljs_builtin_post_increment_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name);
+void __qmljs_builtin_post_increment_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value *index);
+
+void __qmljs_builtin_post_decrement(QV4::Value *result, QV4::Value *val);
+void __qmljs_builtin_post_decrement_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name);
+void __qmljs_builtin_post_decrement_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name);
+void __qmljs_builtin_post_decrement_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index);
+
+void Q_NORETURN __qmljs_builtin_rethrow(QV4::ExecutionContext *context);
+QV4::ExecutionContext *__qmljs_builtin_push_with_scope(const QV4::Value &o, QV4::ExecutionContext *ctx);
+QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(QV4::String *exceptionVarName, const QV4::Value &exceptionValue, QV4::ExecutionContext *ctx);
+QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx);
+void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4::String *name);
+void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val);
+void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length);
+void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter);
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass);
+
+// constructors
+void __qmljs_init_closure(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::Function *clos);
+QV4::Function *__qmljs_register_function(QV4::ExecutionContext *ctx, QV4::String *name,
+ bool hasDirectEval,
+ bool usesArgumentsObject, bool isStrict,
+ bool hasNestedFunctions,
+ QV4::String **formals, unsigned formalCount,
+ QV4::String **locals, unsigned localCount);
+
+// strings
+Q_QML_EXPORT double __qmljs_string_to_number(const QString &s);
+QV4::Value __qmljs_string_from_number(QV4::ExecutionContext *ctx, double number);
+QV4::String *__qmljs_string_concat(QV4::ExecutionContext *ctx, QV4::String *first, QV4::String *second);
+
+// objects
+Q_QML_EXPORT QV4::Value __qmljs_object_default_value(QV4::Object *object, int typeHint);
+void __qmljs_set_activation_property(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value& value);
+void __qmljs_set_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value &value);
+void __qmljs_get_property(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &object, QV4::String *name);
+void __qmljs_get_activation_property(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name);
+
+void __qmljs_call_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc);
+void __qmljs_construct_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc);
+
+
+void __qmljs_get_element(QV4::ExecutionContext *ctx, QV4::Value *retval, const QV4::Value &object, const QV4::Value &index);
+void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::Value &object, const QV4::Value &index, const QV4::Value &value);
+
+// For each
+void __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &in);
+void __qmljs_foreach_next_property_name(QV4::Value *result, const QV4::Value &foreach_iterator);
+
+// type conversion and testing
+QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint);
+QV4::Bool __qmljs_to_boolean(const QV4::Value &value);
+double __qmljs_to_number(const QV4::Value &value);
+QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx);
+Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value);
+void __qmljs_numberToString(QString *result, double num, int radix = 10);
+QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value);
+QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value);
+
+QV4::Bool __qmljs_equal(const QV4::Value &x, const QV4::Value &y);
+Q_QML_EXPORT QV4::Bool __qmljs_strict_equal(const QV4::Value &x, const QV4::Value &y);
+
+// unary operators
+typedef void (*UnaryOpName)(QV4::Value *, const QV4::Value &);
+void __qmljs_uplus(QV4::Value *result, const QV4::Value &value);
+void __qmljs_uminus(QV4::Value *result, const QV4::Value &value);
+void __qmljs_compl(QV4::Value *result, const QV4::Value &value);
+void __qmljs_not(QV4::Value *result, const QV4::Value &value);
+void __qmljs_increment(QV4::Value *result, const QV4::Value &value);
+void __qmljs_decrement(QV4::Value *result, const QV4::Value &value);
+
+void __qmljs_delete_subscript(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, const QV4::Value &index);
+void __qmljs_delete_member(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, QV4::String *name);
+void __qmljs_delete_name(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name);
+
+void Q_NORETURN __qmljs_throw(QV4::ExecutionContext*, const QV4::Value &value);
+
+// binary operators
+typedef void (*BinOp)(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+
+void __qmljs_instanceof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_in(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_bit_or(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_bit_xor(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_bit_and(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_sub(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_mul(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_div(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_mod(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_shl(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_shr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_ushr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_gt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_lt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_ge(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_le(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_eq(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_ne(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_se(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+void __qmljs_sne(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+
+void __qmljs_add_helper(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right);
+
+
+typedef void (*InplaceBinOpName)(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_bit_and_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_bit_or_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_bit_xor_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_add_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_sub_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_mul_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_div_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_mod_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_shl_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_shr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+void __qmljs_inplace_ushr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value);
+
+typedef void (*InplaceBinOpElement)(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_bit_and_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_bit_or_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_bit_xor_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_add_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_sub_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_mul_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_div_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_mod_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_shl_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_shr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+void __qmljs_inplace_ushr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs);
+
+typedef void (*InplaceBinOpMember)(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_bit_and_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_bit_or_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_bit_xor_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_add_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_sub_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_mul_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_div_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_mod_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_shl_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_shr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+void __qmljs_inplace_ushr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs);
+
+typedef QV4::Bool (*CmpOp)(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_gt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_lt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_ge(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_le(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_eq(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_ne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_se(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_sne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right);
+QV4::Bool __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right);
+
+// type conversion and testing
+inline QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint)
+{
+ QV4::Object *o = value.asObject();
+ if (!o)
+ return value;
+ return __qmljs_object_default_value(o, typeHint);
+}
+
+inline double __qmljs_to_number(const QV4::Value &value)
+{
+ switch (value.type()) {
+ case QV4::Value::Undefined_Type:
+ return std::numeric_limits<double>::quiet_NaN();
+ case QV4::Value::Null_Type:
+ return 0;
+ case QV4::Value::Boolean_Type:
+ return (value.booleanValue() ? 1. : 0.);
+ case QV4::Value::Integer_Type:
+ return value.int_32;
+ case QV4::Value::String_Type:
+ return __qmljs_string_to_number(value.stringValue()->toQString());
+ case QV4::Value::Object_Type: {
+ QV4::Value prim = __qmljs_to_primitive(value, QV4::NUMBER_HINT);
+ return __qmljs_to_number(prim);
+ }
+ default: // double
+ return value.doubleValue();
+ }
+}
+
+inline QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx)
+{
+ if (value.isString())
+ return value;
+ return QV4::Value::fromString(__qmljs_convert_to_string(ctx, value));
+}
+
+inline QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value)
+{
+ if (value.isObject())
+ return value;
+ return QV4::Value::fromObject(__qmljs_convert_to_object(ctx, value));
+}
+
+
+inline void __qmljs_uplus(QV4::Value *result, const QV4::Value &value)
+{
+ TRACE1(value);
+
+ *result = value;
+ if (result->tryIntegerConversion())
+ return;
+
+ double n = __qmljs_to_number(value);
+ *result = QV4::Value::fromDouble(n);
+}
+
+inline void __qmljs_uminus(QV4::Value *result, const QV4::Value &value)
+{
+ TRACE1(value);
+
+ // +0 != -0, so we need to convert to double when negating 0
+ if (value.isInteger() && value.integerValue())
+ *result = QV4::Value::fromInt32(-value.integerValue());
+ else {
+ double n = __qmljs_to_number(value);
+ *result = QV4::Value::fromDouble(-n);
+ }
+}
+
+inline void __qmljs_compl(QV4::Value *result, const QV4::Value &value)
+{
+ TRACE1(value);
+
+ int n;
+ if (value.isConvertibleToInt())
+ n = value.int_32;
+ else
+ n = QV4::Value::toInt32(__qmljs_to_number(value));
+
+ *result = QV4::Value::fromInt32(~n);
+}
+
+inline void __qmljs_not(QV4::Value *result, const QV4::Value &value)
+{
+ TRACE1(value);
+
+ bool b = value.toBoolean();
+ *result = QV4::Value::fromBoolean(!b);
+}
+
+// binary operators
+inline void __qmljs_bit_or(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = QV4::Value::fromInt32(left.integerValue() | right.integerValue());
+ return;
+ }
+
+ int lval = QV4::Value::toInt32(__qmljs_to_number(left));
+ int rval = QV4::Value::toInt32(__qmljs_to_number(right));
+ *result = QV4::Value::fromInt32(lval | rval);
+}
+
+inline void __qmljs_bit_xor(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = QV4::Value::fromInt32(left.integerValue() ^ right.integerValue());
+ return;
+ }
+
+ int lval = QV4::Value::toInt32(__qmljs_to_number(left));
+ int rval = QV4::Value::toInt32(__qmljs_to_number(right));
+ *result = QV4::Value::fromInt32(lval ^ rval);
+}
+
+inline void __qmljs_bit_and(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = QV4::Value::fromInt32(left.integerValue() & right.integerValue());
+ return;
+ }
+
+ int lval = QV4::Value::toInt32(__qmljs_to_number(left));
+ int rval = QV4::Value::toInt32(__qmljs_to_number(right));
+ *result = QV4::Value::fromInt32(lval & rval);
+}
+
+inline void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = add_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+
+ if (QV4::Value::bothDouble(left, right)) {
+ *result = QV4::Value::fromDouble(left.doubleValue() + right.doubleValue());
+ return;
+ }
+
+ __qmljs_add_helper(ctx, result, left, right);
+}
+
+inline void __qmljs_sub(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = sub_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = QV4::Value::fromDouble(lval - rval);
+}
+
+inline void __qmljs_mul(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = mul_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = QV4::Value::fromDouble(lval * rval);
+}
+
+inline void __qmljs_div(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = QV4::Value::fromDouble(lval / rval);
+}
+
+inline void __qmljs_mod(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right) && right.integerValue() != 0) {
+ int intRes = left.integerValue() % right.integerValue();
+ if (intRes != 0 || left.integerValue() >= 0) {
+ *result = QV4::Value::fromInt32(intRes);
+ return;
+ }
+ }
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = QV4::Value::fromDouble(std::fmod(lval, rval));
+}
+
+inline void __qmljs_shl(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = QV4::Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f)));
+ return;
+ }
+
+ int lval = QV4::Value::toInt32(__qmljs_to_number(left));
+ unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ *result = QV4::Value::fromInt32(lval << rval);
+}
+
+inline void __qmljs_shr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ if (QV4::Value::integerCompatible(left, right)) {
+ *result = QV4::Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f)));
+ return;
+ }
+
+ int lval = QV4::Value::toInt32(__qmljs_to_number(left));
+ unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ *result = QV4::Value::fromInt32(lval >> rval);
+}
+
+inline void __qmljs_ushr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ uint res;
+ if (QV4::Value::integerCompatible(left, right)) {
+ res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f);
+ } else {
+ unsigned lval = QV4::Value::toUInt32(__qmljs_to_number(left));
+ unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ res = lval >> rval;
+ }
+
+ if (res > INT_MAX)
+ *result = QV4::Value::fromDouble(res);
+ else
+ *result = QV4::Value::fromInt32(res);
+}
+
+inline void __qmljs_gt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right));
+}
+
+inline void __qmljs_lt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right));
+}
+
+inline void __qmljs_ge(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right));
+}
+
+inline void __qmljs_le(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(__qmljs_cmp_le(ctx, left, right));
+}
+
+inline void __qmljs_eq(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right));
+}
+
+inline void __qmljs_ne(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ *result = QV4::Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right));
+}
+
+inline void __qmljs_se(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = __qmljs_strict_equal(left, right);
+ *result = QV4::Value::fromBoolean(r);
+}
+
+inline void __qmljs_sne(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = ! __qmljs_strict_equal(left, right);
+ *result = QV4::Value::fromBoolean(r);
+}
+
+inline QV4::Bool __qmljs_cmp_gt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+ if (QV4::Value::integerCompatible(left, right))
+ return left.integerValue() > right.integerValue();
+
+ QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT);
+ QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT);
+
+ if (QV4::Value::bothDouble(l, r)) {
+ return l.doubleValue() > r.doubleValue();
+ } else if (l.isString() && r.isString()) {
+ return r.stringValue()->compare(l.stringValue());
+ } else {
+ double dl = __qmljs_to_number(l);
+ double dr = __qmljs_to_number(r);
+ return dl > dr;
+ }
+}
+
+inline QV4::Bool __qmljs_cmp_lt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+ if (QV4::Value::integerCompatible(left, right))
+ return left.integerValue() < right.integerValue();
+
+ QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT);
+ QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT);
+
+ if (QV4::Value::bothDouble(l, r)) {
+ return l.doubleValue() < r.doubleValue();
+ } else if (l.isString() && r.isString()) {
+ return l.stringValue()->compare(r.stringValue());
+ } else {
+ double dl = __qmljs_to_number(l);
+ double dr = __qmljs_to_number(r);
+ return dl < dr;
+ }
+}
+
+inline QV4::Bool __qmljs_cmp_ge(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+ if (QV4::Value::integerCompatible(left, right))
+ return left.integerValue() >= right.integerValue();
+
+ QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT);
+ QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT);
+
+ if (QV4::Value::bothDouble(l, r)) {
+ return l.doubleValue() >= r.doubleValue();
+ } else if (l.isString() && r.isString()) {
+ return !l.stringValue()->compare(r.stringValue());
+ } else {
+ double dl = __qmljs_to_number(l);
+ double dr = __qmljs_to_number(r);
+ return dl >= dr;
+ }
+}
+
+inline QV4::Bool __qmljs_cmp_le(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+ if (QV4::Value::integerCompatible(left, right))
+ return left.integerValue() <= right.integerValue();
+
+ QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT);
+ QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT);
+
+ if (QV4::Value::bothDouble(l, r)) {
+ return l.doubleValue() <= r.doubleValue();
+ } else if (l.isString() && r.isString()) {
+ return !r.stringValue()->compare(l.stringValue());
+ } else {
+ double dl = __qmljs_to_number(l);
+ double dr = __qmljs_to_number(r);
+ return dl <= dr;
+ }
+}
+
+inline QV4::Bool __qmljs_cmp_eq(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ // need to test for doubles first as NaN != NaN
+ if (QV4::Value::bothDouble(left, right))
+ return left.doubleValue() == right.doubleValue();
+ if (left.val == right.val)
+ return true;
+ if (left.isString() && right.isString())
+ return left.stringValue()->isEqualTo(right.stringValue());
+
+ return __qmljs_equal(left, right);
+}
+
+inline QV4::Bool __qmljs_cmp_ne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ return !__qmljs_cmp_eq(0, left, right);
+}
+
+inline QV4::Bool __qmljs_cmp_se(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ return __qmljs_strict_equal(left, right);
+}
+
+inline QV4::Bool __qmljs_cmp_sne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ return ! __qmljs_strict_equal(left, right);
+}
+
+inline QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ QV4::Value v;
+ __qmljs_instanceof(ctx, &v, left, right);
+ return v.booleanValue();
+}
+
+inline uint __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right)
+{
+ TRACE2(left, right);
+
+ QV4::Value v;
+ __qmljs_in(ctx, &v, left, right);
+ return v.booleanValue();
+}
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_RUNTIME_H
diff --git a/src/qml/qml/v4/qv4script.cpp b/src/qml/qml/v4/qv4script.cpp
new file mode 100644
index 0000000000..3de218a451
--- /dev/null
+++ b/src/qml/qml/v4/qv4script.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4script_p.h"
+#include "qv4mm_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+#include "qv4context_p.h"
+#include "qv4debugging_p.h"
+#include "qv4exception_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 <QtCore/QDebug>
+#include <QtCore/QString>
+
+using namespace QV4;
+
+struct QmlBindingWrapper : FunctionObject
+{
+ QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml)
+ : FunctionObject(scope, scope->engine->id_eval)
+ , qml(qml)
+ {
+ vtbl = &static_vtbl;
+ function = f;
+ usesArgumentsObject = function->usesArgumentsObject;
+ needsActivation = function->needsActivation();
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
+
+ qmlContext = scope->engine->newQmlContext(this, qml);
+ scope->engine->popContext();
+ }
+
+ static Value call(Managed *that, const Value &, Value *, int);
+ static void markObjects(Managed *m)
+ {
+ QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m);
+ if (wrapper->qml)
+ wrapper->qml->mark();
+ FunctionObject::markObjects(m);
+ wrapper->qmlContext->mark();
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+
+private:
+ Object *qml;
+ CallContext *qmlContext;
+
+};
+
+DEFINE_MANAGED_VTABLE(QmlBindingWrapper);
+
+Value QmlBindingWrapper::call(Managed *that, const Value &, Value *, int)
+{
+ ExecutionEngine *engine = that->engine();
+ QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that);
+
+ CallContext *ctx = This->qmlContext;
+ std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue());
+ engine->pushContext(ctx);
+ Value result = This->function->code(ctx, This->function->codeData);
+ engine->popContext();
+
+ return result;
+
+}
+
+
+void Script::parse()
+{
+ if (parsed)
+ return;
+
+ using namespace QQmlJS;
+
+ parsed = true;
+
+ ExecutionEngine *v4 = scope->engine;
+
+ MemoryManager::GCBlocker gcBlocker(v4->memoryManager);
+
+ V4IR::Module module;
+
+ QQmlJS::Engine ee, *engine = &ee;
+ Lexer lexer(engine);
+ lexer.setCode(sourceCode, line, parseAsBinding);
+ Parser parser(engine);
+
+ const bool parsed = parser.parseProgram();
+
+ DiagnosticMessage *error = 0, **errIt = &error;
+ foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) {
+ if (m.isError()) {
+ *errIt = new DiagnosticMessage;
+ (*errIt)->fileName = sourceFile;
+ (*errIt)->offset = m.loc.offset;
+ (*errIt)->length = m.loc.length;
+ (*errIt)->startLine = m.loc.startLine;
+ (*errIt)->startColumn = m.loc.startColumn;
+ (*errIt)->type = DiagnosticMessage::Error;
+ (*errIt)->message = m.message;
+ errIt = &(*errIt)->next;
+ } else {
+ qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn
+ << ": warning: " << m.message;
+ }
+ }
+ if (error)
+ scope->throwSyntaxError(error);
+
+ if (parsed) {
+ using namespace AST;
+ Program *program = AST::cast<Program *>(parser.rootNode());
+ if (!program) {
+ // if parsing was successful, and we have no program, then
+ // we're done...:
+ return;
+ }
+
+ QStringList inheritedLocals;
+ if (inheritContext)
+ for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i)
+ inheritedLocals.append(*i ? (*i)->toQString() : QString());
+
+ Codegen cg(scope, strictMode);
+ V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module,
+ parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals);
+ QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module));
+ if (inheritContext)
+ isel->setUseFastLookups(false);
+ if (globalIRCode)
+ vmFunction = isel->vmFunction(globalIRCode);
+ }
+
+ if (!vmFunction)
+ // ### FIX file/line number
+ v4->current->throwError(QV4::Value::fromObject(v4->newSyntaxErrorObject(v4->current, 0)));
+}
+
+Value Script::run()
+{
+ if (!parsed)
+ parse();
+ if (!vmFunction)
+ return Value::undefinedValue();
+
+ QV4::ExecutionEngine *engine = scope->engine;
+
+ if (qml.isEmpty()) {
+ TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
+
+ bool strict = scope->strictMode;
+ Lookup *lookups = scope->lookups;
+
+ scope->strictMode = vmFunction->isStrict;
+ scope->lookups = vmFunction->lookups;
+
+ QV4::Value result;
+ try {
+ result = vmFunction->code(scope, vmFunction->codeData);
+ } catch (Exception &e) {
+ scope->strictMode = strict;
+ scope->lookups = lookups;
+ throw;
+ }
+
+ return result;
+
+ } else {
+ FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject());
+ return f->call(Value::undefinedValue(), 0, 0);
+ }
+}
+
+Function *Script::function()
+{
+ if (!parsed)
+ parse();
+ return vmFunction;
+}
+
+Value Script::qmlBinding()
+{
+ if (!parsed)
+ parse();
+ QV4::ExecutionEngine *v4 = scope->engine;
+ return Value::fromObject(new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject()));
+}
+
+QV4::Value Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject)
+{
+ QV4::Script qmlScript(engine, scopeObject, script, QString());
+
+ QV4::ExecutionContext *ctx = engine->current;
+ QV4::Value result = QV4::Value::undefinedValue();
+ try {
+ qmlScript.parse();
+ result = qmlScript.run();
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ }
+ return result;
+}
diff --git a/src/qml/qml/v4/qv4script_p.h b/src/qml/qml/v4/qv4script_p.h
new file mode 100644
index 0000000000..274a87db26
--- /dev/null
+++ b/src/qml/qml/v4/qv4script_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4SCRIPT_H
+#define QV4SCRIPT_H
+
+#include "qv4global_p.h"
+#include "qv4engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ExecutionContext;
+
+struct Q_QML_EXPORT Script {
+ Script(ExecutionContext *scope, 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)
+ , vmFunction(0), parseAsBinding(false) {}
+ Script(ExecutionEngine *engine, Object *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)
+ , qml(Value::fromObject(qml)), vmFunction(0), parseAsBinding(true) {}
+ QString sourceFile;
+ int line;
+ int column;
+ QString sourceCode;
+ ExecutionContext *scope;
+ bool strictMode;
+ bool inheritContext;
+ bool parsed;
+ QV4::PersistentValue qml;
+ Function *vmFunction;
+ bool parseAsBinding;
+
+ void parse();
+ Value run();
+ Value qmlBinding();
+
+ Function *function();
+
+
+ static Value evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4sequenceobject.cpp b/src/qml/qml/v4/qv4sequenceobject.cpp
new file mode 100644
index 0000000000..c4d9a71519
--- /dev/null
+++ b/src/qml/qml/v4/qv4sequenceobject.cpp
@@ -0,0 +1,651 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqml.h>
+
+#include "qv4sequenceobject_p.h"
+
+#include <private/qv4functionobject_p.h>
+#include <private/qv4arrayobject_p.h>
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+// helper function to generate valid warnings if errors occur during sequence operations.
+static void generateWarning(QV4::ExecutionContext *ctx, const QString& description)
+{
+ QQmlEngine *engine = ctx->engine->v8Engine->engine();
+ if (!engine)
+ return;
+ QQmlError retn;
+ retn.setDescription(description);
+
+ QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame();
+
+ retn.setLine(frame.line);
+ retn.setUrl(QUrl(frame.source));
+ QQmlEnginePrivate::warning(engine, retn);
+}
+
+// F(elementType, elementTypeName, sequenceType, defaultValue)
+#define FOREACH_QML_SEQUENCE_TYPE(F) \
+ F(int, Int, QList<int>, 0) \
+ F(qreal, Real, QList<qreal>, 0.0) \
+ F(bool, Bool, QList<bool>, false) \
+ F(QString, String, QList<QString>, QString()) \
+ F(QString, QString, QStringList, QString()) \
+ F(QUrl, Url, QList<QUrl>, QUrl())
+
+static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QString &element)
+{
+ return QV4::Value::fromString(engine, element);
+}
+
+static QV4::Value convertElementToValue(QV4::ExecutionEngine *, int element)
+{
+ return QV4::Value::fromInt32(element);
+}
+
+static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element)
+{
+ return QV4::Value::fromString(engine, element.toString());
+}
+
+static QV4::Value convertElementToValue(QV4::ExecutionEngine *, qreal element)
+{
+ return QV4::Value::fromDouble(element);
+}
+
+static QV4::Value convertElementToValue(QV4::ExecutionEngine *, bool element)
+{
+ return QV4::Value::fromBoolean(element);
+}
+
+static QString convertElementToString(const QString &element)
+{
+ return element;
+}
+
+static QString convertElementToString(int element)
+{
+ return QString::number(element);
+}
+
+static QString convertElementToString(const QUrl &element)
+{
+ return element.toString();
+}
+
+static QString convertElementToString(qreal element)
+{
+ QString qstr;
+ __qmljs_numberToString(&qstr, element, 10);
+ return qstr;
+}
+
+static QString convertElementToString(bool element)
+{
+ if (element)
+ return QStringLiteral("true");
+ else
+ return QStringLiteral("false");
+}
+
+template <typename ElementType> ElementType convertValueToElement(const QV4::Value &value);
+
+template <> QString convertValueToElement(const QV4::Value &value)
+{
+ return value.toQString();
+}
+
+template <> int convertValueToElement(const QV4::Value &value)
+{
+ return value.toInt32();
+}
+
+template <> QUrl convertValueToElement(const QV4::Value &value)
+{
+ return QUrl(value.toQString());
+}
+
+template <> qreal convertValueToElement(const QV4::Value &value)
+{
+ return value.toNumber();
+}
+
+template <> bool convertValueToElement(const QV4::Value &value)
+{
+ return value.toBoolean();
+}
+
+template <typename Container>
+class QQmlSequence : public QV4::Object
+{
+ Q_MANAGED
+public:
+ QQmlSequence(QV4::ExecutionEngine *engine, const Container &container)
+ : QV4::Object(engine)
+ , m_container(container)
+ , m_object(0)
+ , m_propertyIndex(-1)
+ , m_isReference(false)
+ {
+ type = Type_QmlSequence;
+ vtbl = &static_vtbl;
+ prototype = engine->sequencePrototype;
+ init(engine);
+ }
+
+ QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex)
+ : QV4::Object(engine)
+ , m_object(object)
+ , m_propertyIndex(propertyIndex)
+ , m_isReference(true)
+ {
+ type = Type_QmlSequence;
+ vtbl = &static_vtbl;
+ prototype = engine->sequencePrototype;
+ loadReference();
+ init(engine);
+ }
+
+ void init(ExecutionEngine *engine)
+ {
+ defineAccessorProperty(engine, QStringLiteral("length"), method_get_length, method_set_length);
+ }
+
+ QV4::Value containerGetIndexed(uint index, bool *hasProperty)
+ {
+ /* Qt containers have int (rather than uint) allowable indexes. */
+ if (index > INT_MAX) {
+ generateWarning(engine()->current, QLatin1String("Index out of range during indexed get"));
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue();
+ }
+ if (m_isReference) {
+ if (!m_object) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue();
+ }
+ loadReference();
+ }
+ qint32 signedIdx = static_cast<qint32>(index);
+ if (signedIdx < m_container.count()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return convertElementToValue(engine(), m_container.at(signedIdx));
+ }
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue();
+ }
+
+ void containerPutIndexed(uint index, const QV4::Value &value)
+ {
+ /* Qt containers have int (rather than uint) allowable indexes. */
+ if (index > INT_MAX) {
+ generateWarning(engine()->current, QLatin1String("Index out of range during indexed set"));
+ return;
+ }
+
+ if (m_isReference) {
+ if (!m_object)
+ return;
+ loadReference();
+ }
+
+ qint32 signedIdx = static_cast<qint32>(index);
+
+ int count = m_container.count();
+
+ typename Container::value_type element = convertValueToElement<typename Container::value_type>(value);
+
+ if (signedIdx == count) {
+ m_container.append(element);
+ } else if (signedIdx < count) {
+ m_container[signedIdx] = element;
+ } else {
+ /* according to ECMA262r3 we need to insert */
+ /* the value at the given index, increasing length to index+1. */
+ m_container.reserve(signedIdx + 1);
+ while (signedIdx > count++) {
+ m_container.append(typename Container::value_type());
+ }
+ m_container.append(element);
+ }
+
+ if (m_isReference)
+ storeReference();
+ }
+
+ QV4::PropertyAttributes containerQueryIndexed(uint index) const
+ {
+ /* Qt containers have int (rather than uint) allowable indexes. */
+ if (index > INT_MAX) {
+ generateWarning(engine()->current, QLatin1String("Index out of range during indexed query"));
+ return QV4::Attr_Invalid;
+ }
+ if (m_isReference) {
+ if (!m_object)
+ return QV4::Attr_Invalid;
+ loadReference();
+ }
+ qint32 signedIdx = static_cast<qint32>(index);
+ return (signedIdx < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid;
+ }
+
+ Property *containerAdvanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs)
+ {
+ *name = 0;
+ *index = UINT_MAX;
+
+ if (m_isReference) {
+ if (!m_object)
+ return QV4::Object::advanceIterator(this, it, name, index, attrs);
+ loadReference();
+ }
+
+ if (it->arrayIndex < m_container.count()) {
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ *index = it->arrayIndex;
+ ++it->arrayIndex;
+ it->tmpDynamicProperty.value = convertElementToValue(engine(), m_container.at(*index));
+ return &it->tmpDynamicProperty;
+ }
+ return QV4::Object::advanceIterator(this, it, name, index, attrs);
+ }
+
+ bool containerDeleteIndexedProperty(uint index)
+ {
+ /* Qt containers have int (rather than uint) allowable indexes. */
+ if (index > INT_MAX)
+ return false;
+ if (m_isReference) {
+ if (!m_object)
+ return false;
+ loadReference();
+ }
+ qint32 signedIdx = static_cast<qint32>(index);
+
+ if (signedIdx >= m_container.count())
+ return false;
+
+ /* according to ECMA262r3 it should be Undefined, */
+ /* but we cannot, so we insert a default-value instead. */
+ m_container.replace(signedIdx, typename Container::value_type());
+
+ if (m_isReference)
+ storeReference();
+
+ return true;
+ }
+
+ bool containerIsEqualTo(Managed *other)
+ {
+ QQmlSequence<Container> *otherSequence = other->as<QQmlSequence<Container> >();
+ if (!otherSequence)
+ return false;
+ if (m_isReference && otherSequence->m_isReference) {
+ return m_object == otherSequence->m_object && m_propertyIndex == otherSequence->m_propertyIndex;
+ } else if (!m_isReference && !otherSequence->m_isReference) {
+ return this == otherSequence;
+ }
+ return false;
+ }
+
+ struct DefaultCompareFunctor
+ {
+ bool operator()(typename Container::value_type lhs, typename Container::value_type rhs)
+ {
+ return convertElementToString(lhs) < convertElementToString(rhs);
+ }
+ };
+
+ struct CompareFunctor
+ {
+ CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn)
+ : m_ctx(ctx), m_compareFn(compareFn)
+ {}
+
+ bool operator()(typename Container::value_type lhs, typename Container::value_type rhs)
+ {
+ QV4::Managed *fun = this->m_compareFn.asManaged();
+ QV4::Value argv[2] = {
+ convertElementToValue(this->m_ctx->engine, lhs),
+ convertElementToValue(this->m_ctx->engine, rhs)
+ };
+ QV4::Value result = fun->call(QV4::Value::fromObject(this->m_ctx->engine->globalObject), argv, 2);
+ return result.toNumber() < 0;
+ }
+
+ private:
+ QV4::ExecutionContext *m_ctx;
+ QV4::Value m_compareFn;
+ };
+
+ void sort(QV4::SimpleCallContext *ctx)
+ {
+ if (m_isReference) {
+ if (!m_object)
+ return;
+ loadReference();
+ }
+
+ if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) {
+ QV4::Value compareFn = ctx->arguments[0];
+ CompareFunctor cf(ctx, compareFn);
+ qSort(m_container.begin(), m_container.end(), cf);
+ } else {
+ DefaultCompareFunctor cf;
+ qSort(m_container.begin(), m_container.end(), cf);
+ }
+
+ if (m_isReference)
+ storeReference();
+ }
+
+ static QV4::Value method_get_length(QV4::SimpleCallContext *ctx)
+ {
+ QQmlSequence<Container> *This = ctx->thisObject.as<QQmlSequence<Container> >();
+ if (!This)
+ ctx->throwTypeError();
+
+ if (This->m_isReference) {
+ if (!This->m_object)
+ return QV4::Value::fromInt32(0);
+ This->loadReference();
+ }
+ return QV4::Value::fromInt32(This->m_container.count());
+ }
+
+ static QV4::Value method_set_length(QV4::SimpleCallContext* ctx)
+ {
+ QQmlSequence<Container> *This = ctx->thisObject.as<QQmlSequence<Container> >();
+ if (!This)
+ ctx->throwTypeError();
+
+ quint32 newLength = ctx->arguments[0].toUInt32();
+ /* Qt containers have int (rather than uint) allowable indexes. */
+ if (newLength > INT_MAX) {
+ generateWarning(ctx, QLatin1String("Index out of range during length set"));
+ return QV4::Value::undefinedValue();
+ }
+ /* Read the sequence from the QObject property if we're a reference */
+ if (This->m_isReference) {
+ if (!This->m_object)
+ return QV4::Value::undefinedValue();
+ This->loadReference();
+ }
+ /* Determine whether we need to modify the sequence */
+ qint32 newCount = static_cast<qint32>(newLength);
+ qint32 count = This->m_container.count();
+ if (newCount == count) {
+ return QV4::Value::undefinedValue();
+ } else if (newCount > count) {
+ /* according to ECMA262r3 we need to insert */
+ /* undefined values increasing length to newLength. */
+ /* We cannot, so we insert default-values instead. */
+ This->m_container.reserve(newCount);
+ while (newCount > count++) {
+ This->m_container.append(typename Container::value_type());
+ }
+ } else {
+ /* according to ECMA262r3 we need to remove */
+ /* elements until the sequence is the required length. */
+ while (newCount < count) {
+ count--;
+ This->m_container.removeAt(count);
+ }
+ }
+ /* write back if required. */
+ if (This->m_isReference) {
+ /* write back. already checked that object is non-null, so skip that check here. */
+ This->storeReference();
+ }
+ return QV4::Value::undefinedValue();
+ }
+
+ QVariant toVariant() const
+ { return QVariant::fromValue<Container>(m_container); }
+
+ static QVariant toVariant(QV4::ArrayObject *array)
+ {
+ Container result;
+ uint32_t length = array->arrayLength();
+ for (uint32_t i = 0; i < length; ++i)
+ result << convertValueToElement<typename Container::value_type>(array->getIndexed(i));
+ return QVariant::fromValue(result);
+ }
+
+private:
+ void loadReference() const
+ {
+ Q_ASSERT(m_object);
+ Q_ASSERT(m_isReference);
+ void *a[] = { &m_container, 0 };
+ QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a);
+ }
+
+ void storeReference()
+ {
+ Q_ASSERT(m_object);
+ Q_ASSERT(m_isReference);
+ int status = -1;
+ QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding;
+ void *a[] = { &m_container, 0, &status, &flags };
+ QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a);
+ }
+
+ mutable Container m_container;
+ QPointer<QObject> m_object;
+ int m_propertyIndex;
+ bool m_isReference;
+
+ static QV4::Value getIndexed(QV4::Managed *that, uint index, bool *hasProperty)
+ { return static_cast<QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); }
+ static void putIndexed(Managed *that, uint index, const QV4::Value &value)
+ { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); }
+ static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index)
+ { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); }
+ static bool deleteIndexedProperty(QV4::Managed *that, uint index)
+ { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); }
+ static bool isEqualTo(Managed *that, Managed *other)
+ { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); }
+ static Property *advanceIterator(Managed *that, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs)
+ { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, attrs); }
+
+ static void destroy(Managed *that)
+ {
+ static_cast<QQmlSequence<Container> *>(that)->~QQmlSequence<Container>();
+ }
+};
+
+typedef QQmlSequence<QStringList> QQmlQStringList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlQStringList);
+typedef QQmlSequence<QList<QString> > QQmlStringList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlStringList);
+typedef QQmlSequence<QList<int> > QQmlIntList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlIntList);
+typedef QQmlSequence<QList<QUrl> > QQmlUrlList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlUrlList);
+typedef QQmlSequence<QList<bool> > QQmlBoolList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlBoolList);
+typedef QQmlSequence<QList<qreal> > QQmlRealList;
+template<>
+DEFINE_MANAGED_VTABLE(QQmlRealList);
+
+#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType);
+SequencePrototype::SequencePrototype(ExecutionEngine *engine)
+ : QV4::Object(engine)
+{
+ prototype = engine->arrayPrototype;
+ FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE)
+}
+#undef REGISTER_QML_SEQUENCE_METATYPE
+
+void SequencePrototype::init(QV4::ExecutionEngine *engine)
+{
+ defineDefaultProperty(engine, QStringLiteral("sort"), method_sort, 1);
+ defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0);
+}
+
+QV4::Value SequencePrototype::method_sort(QV4::SimpleCallContext *ctx)
+{
+ QV4::Object *o = ctx->thisObject.asObject();
+ if (!o || !o->isListType())
+ ctx->throwTypeError();
+
+ if (ctx->argumentCount >= 2)
+ return ctx->thisObject;
+
+#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
+ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \
+ s->sort(ctx); \
+ } else
+
+ FOREACH_QML_SEQUENCE_TYPE(CALL_SORT)
+
+#undef CALL_SORT
+ return ctx->thisObject;
+}
+
+#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \
+ if (sequenceTypeId == qMetaTypeId<SequenceType>()) { \
+ return true; \
+ } else
+
+bool SequencePrototype::isSequenceType(int sequenceTypeId)
+{
+ FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; }
+}
+#undef IS_SEQUENCE
+
+#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
+ if (sequenceType == qMetaTypeId<SequenceType>()) { \
+ QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, object, propertyIndex); \
+ return QV4::Value::fromObject(obj); \
+ } else
+
+QV4::Value SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
+{
+ // This function is called when the property is a QObject Q_PROPERTY of
+ // the given sequence type. Internally we store a typed-sequence
+ // (as well as object ptr + property index for updated-read and write-back)
+ // and so access/mutate avoids variant conversion.
+ *succeeded = true;
+ FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); }
+}
+#undef NEW_REFERENCE_SEQUENCE
+
+#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
+ if (sequenceType == qMetaTypeId<SequenceType>()) { \
+ QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, v.value<SequenceType >()); \
+ return QV4::Value::fromObject(obj); \
+ } else
+
+QV4::Value SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded)
+{
+ // This function is called when assigning a sequence value to a normal JS var
+ // in a JS block. Internally, we store a sequence of the specified type.
+ // Access and mutation is extremely fast since it will not need to modify any
+ // QObject property.
+ int sequenceType = v.userType();
+ *succeeded = true;
+ FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); }
+}
+#undef NEW_COPY_SEQUENCE
+
+#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \
+ if (QQml##ElementTypeName##List *list = object->as<QQml##ElementTypeName##List>()) \
+ return list->toVariant(); \
+ else
+
+QVariant SequencePrototype::toVariant(QV4::Object *object)
+{
+ Q_ASSERT(object->isListType());
+ FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); }
+}
+
+#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \
+ if (typeHint == qMetaTypeId<SequenceType>()) { \
+ return QQml##ElementTypeName##List::toVariant(a); \
+ } else
+
+QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded)
+{
+ *succeeded = true;
+
+ QV4::ArrayObject *a = array.asArrayObject();
+ if (!a) {
+ *succeeded = false;
+ return QVariant();
+ }
+ FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); }
+}
+
+#undef SEQUENCE_TO_VARIANT
+
+#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \
+ if (object->as<QQml##ElementTypeName##List>()) { \
+ return qMetaTypeId<SequenceType>(); \
+ } else
+
+int SequencePrototype::metaTypeForSequence(QV4::Object *object)
+{
+ FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE)
+ /*else*/ {
+ return -1;
+ }
+}
+
+#undef MAP_META_TYPE
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4sequenceobject_p.h b/src/qml/qml/v4/qv4sequenceobject_p.h
new file mode 100644
index 0000000000..2cade45092
--- /dev/null
+++ b/src/qml/qml/v4/qv4sequenceobject_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SEQUENCEWRAPPER_P_H
+#define QV4SEQUENCEWRAPPER_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/qglobal.h>
+#include <QtCore/qvariant.h>
+
+#include "qv4value_p.h"
+#include "qv4object_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct SequencePrototype : public QV4::Object
+{
+ SequencePrototype(QV4::ExecutionEngine *engine);
+
+ void init(QV4::ExecutionEngine *engine);
+
+ static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx)
+ {
+ return QV4::Value::fromString(ctx->thisObject.toString(ctx));
+ }
+
+ static QV4::Value method_sort(QV4::SimpleCallContext *ctx);
+
+ static bool isSequenceType(int sequenceTypeId);
+ static QV4::Value newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
+ static QV4::Value fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded);
+ static int metaTypeForSequence(QV4::Object *object);
+ static QVariant toVariant(QV4::Object *object);
+ static QVariant toVariant(const QV4::Value &array, int typeHint, bool *succeeded);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4SEQUENCEWRAPPER_P_H
diff --git a/src/qml/qml/v4/qv4serialize.cpp b/src/qml/qml/v4/qv4serialize.cpp
new file mode 100644
index 0000000000..f7389dc6d7
--- /dev/null
+++ b/src/qml/qml/v4/qv4serialize.cpp
@@ -0,0 +1,399 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4serialize_p.h"
+
+#include <private/qv8engine_p.h>
+#include <private/qqmllistmodel_p.h>
+#include <private/qqmllistmodelworkeragent_p.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4regexpobject_p.h>
+#include <private/qv4sequenceobject_p.h>
+#include <private/qv4objectproto_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+// We allow the following JavaScript types to be passed between the main and
+// the secondary thread:
+// + undefined
+// + null
+// + Boolean
+// + String
+// + Function
+// + Array
+// + "Simple" Objects
+// + Number
+// + Date
+// + RegExp
+// <quint8 type><quint24 size><data>
+
+enum Type {
+ WorkerUndefined,
+ WorkerNull,
+ WorkerTrue,
+ WorkerFalse,
+ WorkerString,
+ WorkerFunction,
+ WorkerArray,
+ WorkerObject,
+ WorkerInt32,
+ WorkerUint32,
+ WorkerNumber,
+ WorkerDate,
+ WorkerRegexp,
+ WorkerListModel,
+ WorkerSequence
+};
+
+static inline quint32 valueheader(Type type, quint32 size = 0)
+{
+ return quint8(type) << 24 | (size & 0xFFFFFF);
+}
+
+static inline Type headertype(quint32 header)
+{
+ return (Type)(header >> 24);
+}
+
+static inline quint32 headersize(quint32 header)
+{
+ return header & 0xFFFFFF;
+}
+
+static inline void push(QByteArray &data, quint32 value)
+{
+ data.append((const char *)&value, sizeof(quint32));
+}
+
+static inline void push(QByteArray &data, double value)
+{
+ data.append((const char *)&value, sizeof(double));
+}
+
+static inline void push(QByteArray &data, void *ptr)
+{
+ data.append((const char *)&ptr, sizeof(void *));
+}
+
+static inline void reserve(QByteArray &data, int extra)
+{
+ data.reserve(data.size() + extra);
+}
+
+static inline quint32 popUint32(const char *&data)
+{
+ quint32 rv = *((quint32 *)data);
+ data += sizeof(quint32);
+ return rv;
+}
+
+static inline double popDouble(const char *&data)
+{
+ double rv = *((double *)data);
+ data += sizeof(double);
+ return rv;
+}
+
+static inline void *popPtr(const char *&data)
+{
+ void *rv = *((void **)data);
+ data += sizeof(void *);
+ return rv;
+}
+
+// XXX TODO: Check that worker script is exception safe in the case of
+// serialization/deserialization failures
+
+#define ALIGN(size) (((size) + 3) & ~3)
+void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine)
+{
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ if (v.isEmpty()) {
+ } else if (v.isUndefined()) {
+ push(data, valueheader(WorkerUndefined));
+ } else if (v.isNull()) {
+ push(data, valueheader(WorkerNull));
+ } else if (v.isBoolean()) {
+ push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse));
+ } else if (QV4::String *s = v.asString()) {
+ const QString &qstr = s->toQString();
+ int length = qstr.length();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ int utf16size = ALIGN(length * sizeof(uint16_t));
+
+ reserve(data, utf16size + sizeof(quint32));
+ push(data, valueheader(WorkerString, length));
+
+ int offset = data.size();
+ data.resize(data.size() + utf16size);
+ char *buffer = data.data() + offset;
+
+ memcpy(buffer, qstr.constData(), length*sizeof(QChar));
+ } else if (v.asFunctionObject()) {
+ // XXX TODO: Implement passing function objects between the main and
+ // worker scripts
+ push(data, valueheader(WorkerUndefined));
+ } else if (QV4::ArrayObject *array = v.asArrayObject()) {
+ uint32_t length = array->arrayLength();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ reserve(data, sizeof(quint32) + length * sizeof(quint32));
+ push(data, valueheader(WorkerArray, length));
+ for (uint32_t ii = 0; ii < length; ++ii)
+ serialize(data, array->getIndexed(ii), engine);
+ } else if (v.isInteger()) {
+ reserve(data, 2 * sizeof(quint32));
+ push(data, valueheader(WorkerInt32));
+ push(data, (quint32)v.integerValue());
+// } else if (v->IsUint32()) {
+// reserve(data, 2 * sizeof(quint32));
+// push(data, valueheader(WorkerUint32));
+// push(data, v->Uint32Value());
+ } else if (v.isNumber()) {
+ reserve(data, sizeof(quint32) + sizeof(double));
+ push(data, valueheader(WorkerNumber));
+ push(data, v.asDouble());
+ } else if (QV4::DateObject *d = v.asDateObject()) {
+ reserve(data, sizeof(quint32) + sizeof(double));
+ push(data, valueheader(WorkerDate));
+ push(data, d->value.asDouble());
+ } else if (QV4::RegExpObject *re = v.as<RegExpObject>()) {
+ quint32 flags = re->flags();
+ QString pattern = re->source();
+ int length = pattern.length() + 1;
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ int utf16size = ALIGN(length * sizeof(uint16_t));
+
+ reserve(data, sizeof(quint32) + utf16size);
+ push(data, valueheader(WorkerRegexp, flags));
+ push(data, (quint32)length);
+
+ int offset = data.size();
+ data.resize(data.size() + utf16size);
+ char *buffer = data.data() + offset;
+
+ memcpy(buffer, pattern.constData(), length*sizeof(QChar));
+ } else if (QV4::QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
+ // XXX TODO: Generalize passing objects between the main thread and worker scripts so
+ // that others can trivially plug in their elements.
+ QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
+ if (lm && lm->agent()) {
+ QQmlListModelWorkerAgent *agent = lm->agent();
+ agent->addref();
+ push(data, valueheader(WorkerListModel));
+ push(data, (void *)agent);
+ return;
+ }
+ // No other QObject's are allowed to be sent
+ push(data, valueheader(WorkerUndefined));
+ } else if (QV4::Object *o = v.asObject()) {
+
+ if (o->isListType()) {
+ // valid sequence. we generate a length (sequence length + 1 for the sequence type)
+ uint32_t seqLength = o->get(v4->id_length).toUInt32();
+ uint32_t length = seqLength + 1;
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ reserve(data, sizeof(quint32) + length * sizeof(quint32));
+ push(data, valueheader(WorkerSequence, length));
+ serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
+ for (uint32_t ii = 0; ii < seqLength; ++ii)
+ serialize(data, o->getIndexed(ii), engine); // sequence elements
+
+ return;
+ }
+
+ // regular object
+ QV4::ArrayObject *properties = QV4::ObjectPrototype::getOwnPropertyNames(v4, v);
+ quint32 length = properties->arrayLength();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ push(data, valueheader(WorkerObject, length));
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ for (quint32 ii = 0; ii < length; ++ii) {
+ QV4::String *s = properties->getIndexed(ii).asString();
+ serialize(data, QV4::Value::fromString(s), engine);
+
+ bool hasCaught = false;
+ QV4::ExecutionContext *ctx = v4->current;
+ QV4::Value val = QV4::Value::undefinedValue();
+ try {
+ val = o->get(s);
+ } catch (QV4::Exception &e) {
+ e.accept(ctx);
+ }
+
+ serialize(data, val, engine);
+ }
+ return;
+ } else {
+ push(data, valueheader(WorkerUndefined));
+ }
+}
+
+QV4::Value Serialize::deserialize(const char *&data, QV8Engine *engine)
+{
+ quint32 header = popUint32(data);
+ Type type = headertype(header);
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+
+ switch (type) {
+ case WorkerUndefined:
+ return QV4::Value::undefinedValue();
+ case WorkerNull:
+ return QV4::Value::nullValue();
+ case WorkerTrue:
+ return QV4::Value::fromBoolean(true);
+ case WorkerFalse:
+ return QV4::Value::fromBoolean(false);
+ case WorkerString:
+ {
+ quint32 size = headersize(header);
+ QString qstr((QChar *)data, size);
+ data += ALIGN(size * sizeof(uint16_t));
+ return QV4::Value::fromString(v4->newString(qstr));
+ }
+ case WorkerFunction:
+ Q_ASSERT(!"Unreachable");
+ break;
+ case WorkerArray:
+ {
+ quint32 size = headersize(header);
+ QV4::ArrayObject *a = v4->newArrayObject();
+ for (quint32 ii = 0; ii < size; ++ii) {
+ a->putIndexed(ii, deserialize(data, engine));
+ }
+ return QV4::Value::fromObject(a);
+ }
+ case WorkerObject:
+ {
+ quint32 size = headersize(header);
+ QV4::Object *o = v4->newObject();
+ for (quint32 ii = 0; ii < size; ++ii) {
+ QV4::Value name = deserialize(data, engine);
+ QV4::Value value = deserialize(data, engine);
+ o->put(name.asString(), value);
+ }
+ return QV4::Value::fromObject(o);
+ }
+ case WorkerInt32:
+ return QV4::Value::fromInt32((qint32)popUint32(data));
+ case WorkerUint32:
+ return QV4::Value::fromUInt32(popUint32(data));
+ case WorkerNumber:
+ return QV4::Value::fromDouble(popDouble(data));
+ case WorkerDate:
+ return QV4::Value::fromObject(v4->newDateObject(QV4::Value::fromDouble(popDouble(data))));
+ case WorkerRegexp:
+ {
+ quint32 flags = headersize(header);
+ quint32 length = popUint32(data);
+ QString pattern = QString((QChar *)data, length - 1);
+ data += ALIGN(length * sizeof(uint16_t));
+ return QV4::Value::fromObject(v4->newRegExpObject(pattern, flags));
+ }
+ case WorkerListModel:
+ {
+ void *ptr = popPtr(data);
+ QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr;
+ QV4::Value rv = QV4::QObjectWrapper::wrap(v4, agent);
+ // ### Find a better solution then the ugly property
+ QQmlListModelWorkerAgent::VariantRef ref(agent);
+ QVariant var = qVariantFromValue(ref);
+ rv.asObject()->defineReadonlyProperty(v4->newString("__qml:hidden:ref"), engine->fromVariant(var));
+
+ agent->release();
+ agent->setV8Engine(engine);
+ return rv;
+ }
+ case WorkerSequence:
+ {
+ bool succeeded = false;
+ quint32 length = headersize(header);
+ quint32 seqLength = length - 1;
+ int sequenceType = deserialize(data, engine).integerValue();
+ QV4::ArrayObject *array = v4->newArrayObject();
+ array->arrayReserve(seqLength);
+ array->arrayDataLen = seqLength;
+ for (quint32 ii = 0; ii < seqLength; ++ii)
+ array->arrayData[ii].value = deserialize(data, engine);
+ array->setArrayLengthUnchecked(seqLength);
+ QVariant seqVariant = QV4::SequencePrototype::toVariant(QV4::Value::fromObject(array), sequenceType, &succeeded);
+ return QV4::SequencePrototype::fromVariant(v4, seqVariant, &succeeded);
+ }
+ }
+ Q_ASSERT(!"Unreachable");
+ return QV4::Value::undefinedValue();
+}
+
+QByteArray Serialize::serialize(const QV4::Value &value, QV8Engine *engine)
+{
+ QByteArray rv;
+ serialize(rv, value, engine);
+ return rv;
+}
+
+QV4::Value Serialize::deserialize(const QByteArray &data, QV8Engine *engine)
+{
+ const char *stream = data.constData();
+ return deserialize(stream, engine);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4serialize_p.h
index 5b6cee2a55..5a04c9d25f 100644
--- a/src/qml/qml/v4/qv4compiler_p.h
+++ b/src/qml/qml/v4/qv4serialize_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QV4COMPILER_P_H
-#define QV4COMPILER_P_H
+#ifndef QV4SERIALIZE_P_H
+#define QV4SERIALIZE_P_H
//
// W A R N I N G
@@ -53,53 +53,28 @@
// We mean it.
//
-#include <private/qqmlexpression_p.h>
-#include <private/qqmlbinding_p.h>
-#include <private/qqmlcompiler_p.h>
-
-#include <private/qv8_p.h>
-
-Q_DECLARE_METATYPE(v8::Handle<v8::Value>)
+#include <QtCore/qbytearray.h>
+#include <private/qv4value_p.h>
QT_BEGIN_NAMESPACE
-class QQmlTypeNameCache;
-class QV4CompilerPrivate;
-class Q_AUTOTEST_EXPORT QV4Compiler
-{
-public:
- QV4Compiler();
- ~QV4Compiler();
-
- // Returns true if bindings were compiled
- bool isValid() const;
+class QV8Engine;
- struct Expression
- {
- Expression(const QQmlImports &imp) : imports(imp) {}
- QQmlScript::Object *component;
- QQmlScript::Object *context;
- QQmlScript::Property *property;
- QQmlScript::Variant expression;
- QQmlCompilerTypes::IdList *ids;
- QQmlTypeNameCache *importCache;
- QQmlImports imports;
- };
+namespace QV4 {
- // -1 on failure, otherwise the binding index to use
- int compile(const Expression &, QQmlEnginePrivate *, bool *);
+class Serialize {
+public:
- // Returns the compiled program
- QByteArray program() const;
+ static QByteArray serialize(const Value &, QV8Engine *);
+ static Value deserialize(const QByteArray &, QV8Engine *);
- static void dump(const QByteArray &);
- static void enableBindingsTest(bool);
- static void enableV4(bool);
private:
- QV4CompilerPrivate *d;
+ static void serialize(QByteArray &, const Value &, QV8Engine *);
+ static Value deserialize(const char *&, QV8Engine *);
};
-QT_END_NAMESPACE
+}
-#endif // QV4COMPILER_P_H
+QT_END_NAMESPACE
+#endif // QV8WORKER_P_H
diff --git a/src/qml/qml/v4/qv4sparsearray.cpp b/src/qml/qml/v4/qv4sparsearray.cpp
new file mode 100644
index 0000000000..835a0d004f
--- /dev/null
+++ b/src/qml/qml/v4/qv4sparsearray.cpp
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4sparsearray_p.h"
+#include "qv4runtime_p.h"
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <stdlib.h>
+
+#ifdef QT_QMAP_DEBUG
+# include <qstring.h>
+# include <qvector.h>
+#endif
+
+using namespace QV4;
+
+bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const
+{
+ Value v1 = p1.value;
+ Value v2 = p2.value;
+
+ if (v1.isUndefined())
+ return false;
+ if (v2.isUndefined())
+ return true;
+ if (!m_comparefn.isUndefined()) {
+ Value args[] = { v1, v2 };
+ Value result = Value::undefinedValue();
+ __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2);
+ return result.toNumber() <= 0;
+ }
+ return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString();
+}
+
+
+const SparseArrayNode *SparseArrayNode::nextNode() const
+{
+ const SparseArrayNode *n = this;
+ if (n->right) {
+ n = n->right;
+ while (n->left)
+ n = n->left;
+ } else {
+ const SparseArrayNode *y = n->parent();
+ while (y && n == y->right) {
+ n = y;
+ y = n->parent();
+ }
+ n = y;
+ }
+ return n;
+}
+
+const SparseArrayNode *SparseArrayNode::previousNode() const
+{
+ const SparseArrayNode *n = this;
+ if (n->left) {
+ n = n->left;
+ while (n->right)
+ n = n->right;
+ } else {
+ const SparseArrayNode *y = n->parent();
+ while (y && n == y->left) {
+ n = y;
+ y = n->parent();
+ }
+ n = y;
+ }
+ return n;
+}
+
+SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const
+{
+ SparseArrayNode *n = d->createNode(size_left, 0, false);
+ n->value = value;
+ n->setColor(color());
+ if (left) {
+ n->left = left->copy(d);
+ n->left->setParent(n);
+ } else {
+ n->left = 0;
+ }
+ if (right) {
+ n->right = right->copy(d);
+ n->right->setParent(n);
+ } else {
+ n->right = 0;
+ }
+ return n;
+}
+
+/*
+ x y
+ \ / \
+ y --> x b
+ / \ \
+ a b a
+*/
+void SparseArray::rotateLeft(SparseArrayNode *x)
+{
+ SparseArrayNode *&root = header.left;
+ SparseArrayNode *y = x->right;
+ x->right = y->left;
+ if (y->left != 0)
+ y->left->setParent(x);
+ y->setParent(x->parent());
+ if (x == root)
+ root = y;
+ else if (x == x->parent()->left)
+ x->parent()->left = y;
+ else
+ x->parent()->right = y;
+ y->left = x;
+ x->setParent(y);
+ y->size_left += x->size_left;
+}
+
+
+/*
+ x y
+ / / \
+ y --> a x
+ / \ /
+ a b b
+*/
+void SparseArray::rotateRight(SparseArrayNode *x)
+{
+ SparseArrayNode *&root = header.left;
+ SparseArrayNode *y = x->left;
+ x->left = y->right;
+ if (y->right != 0)
+ y->right->setParent(x);
+ y->setParent(x->parent());
+ if (x == root)
+ root = y;
+ else if (x == x->parent()->right)
+ x->parent()->right = y;
+ else
+ x->parent()->left = y;
+ y->right = x;
+ x->setParent(y);
+ x->size_left -= y->size_left;
+}
+
+
+void SparseArray::rebalance(SparseArrayNode *x)
+{
+ SparseArrayNode *&root = header.left;
+ x->setColor(SparseArrayNode::Red);
+ while (x != root && x->parent()->color() == SparseArrayNode::Red) {
+ if (x->parent() == x->parent()->parent()->left) {
+ SparseArrayNode *y = x->parent()->parent()->right;
+ if (y && y->color() == SparseArrayNode::Red) {
+ x->parent()->setColor(SparseArrayNode::Black);
+ y->setColor(SparseArrayNode::Black);
+ x->parent()->parent()->setColor(SparseArrayNode::Red);
+ x = x->parent()->parent();
+ } else {
+ if (x == x->parent()->right) {
+ x = x->parent();
+ rotateLeft(x);
+ }
+ x->parent()->setColor(SparseArrayNode::Black);
+ x->parent()->parent()->setColor(SparseArrayNode::Red);
+ rotateRight (x->parent()->parent());
+ }
+ } else {
+ SparseArrayNode *y = x->parent()->parent()->left;
+ if (y && y->color() == SparseArrayNode::Red) {
+ x->parent()->setColor(SparseArrayNode::Black);
+ y->setColor(SparseArrayNode::Black);
+ x->parent()->parent()->setColor(SparseArrayNode::Red);
+ x = x->parent()->parent();
+ } else {
+ if (x == x->parent()->left) {
+ x = x->parent();
+ rotateRight(x);
+ }
+ x->parent()->setColor(SparseArrayNode::Black);
+ x->parent()->parent()->setColor(SparseArrayNode::Red);
+ rotateLeft(x->parent()->parent());
+ }
+ }
+ }
+ root->setColor(SparseArrayNode::Black);
+}
+
+void SparseArray::deleteNode(SparseArrayNode *z)
+{
+ SparseArrayNode *&root = header.left;
+ SparseArrayNode *y = z;
+ SparseArrayNode *x;
+ SparseArrayNode *x_parent;
+ if (y->left == 0) {
+ x = y->right;
+ if (y == mostLeftNode) {
+ if (x)
+ mostLeftNode = x; // It cannot have (left) children due the red black invariant.
+ else
+ mostLeftNode = y->parent();
+ }
+ } else {
+ if (y->right == 0) {
+ x = y->left;
+ } else {
+ y = y->right;
+ while (y->left != 0)
+ y = y->left;
+ x = y->right;
+ }
+ }
+ if (y != z) {
+ z->left->setParent(y);
+ y->left = z->left;
+ if (y != z->right) {
+ x_parent = y->parent();
+ if (x)
+ x->setParent(y->parent());
+ y->parent()->left = x;
+ y->right = z->right;
+ z->right->setParent(y);
+ } else {
+ x_parent = y;
+ }
+ if (root == z)
+ root = y;
+ else if (z->parent()->left == z)
+ z->parent()->left = y;
+ else
+ z->parent()->right = y;
+ y->setParent(z->parent());
+ // Swap the colors
+ SparseArrayNode::Color c = y->color();
+ y->setColor(z->color());
+ z->setColor(c);
+ y = z;
+ } else {
+ x_parent = y->parent();
+ if (x)
+ x->setParent(y->parent());
+ if (root == z)
+ root = x;
+ else if (z->parent()->left == z)
+ z->parent()->left = x;
+ else
+ z->parent()->right = x;
+ }
+ if (y->color() != SparseArrayNode::Red) {
+ while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) {
+ if (x == x_parent->left) {
+ SparseArrayNode *w = x_parent->right;
+ if (w->color() == SparseArrayNode::Red) {
+ w->setColor(SparseArrayNode::Black);
+ x_parent->setColor(SparseArrayNode::Red);
+ rotateLeft(x_parent);
+ w = x_parent->right;
+ }
+ if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) &&
+ (w->right == 0 || w->right->color() == SparseArrayNode::Black)) {
+ w->setColor(SparseArrayNode::Red);
+ x = x_parent;
+ x_parent = x_parent->parent();
+ } else {
+ if (w->right == 0 || w->right->color() == SparseArrayNode::Black) {
+ if (w->left)
+ w->left->setColor(SparseArrayNode::Black);
+ w->setColor(SparseArrayNode::Red);
+ rotateRight(w);
+ w = x_parent->right;
+ }
+ w->setColor(x_parent->color());
+ x_parent->setColor(SparseArrayNode::Black);
+ if (w->right)
+ w->right->setColor(SparseArrayNode::Black);
+ rotateLeft(x_parent);
+ break;
+ }
+ } else {
+ SparseArrayNode *w = x_parent->left;
+ if (w->color() == SparseArrayNode::Red) {
+ w->setColor(SparseArrayNode::Black);
+ x_parent->setColor(SparseArrayNode::Red);
+ rotateRight(x_parent);
+ w = x_parent->left;
+ }
+ if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) &&
+ (w->left == 0 || w->left->color() == SparseArrayNode::Black)) {
+ w->setColor(SparseArrayNode::Red);
+ x = x_parent;
+ x_parent = x_parent->parent();
+ } else {
+ if (w->left == 0 || w->left->color() == SparseArrayNode::Black) {
+ if (w->right)
+ w->right->setColor(SparseArrayNode::Black);
+ w->setColor(SparseArrayNode::Red);
+ rotateLeft(w);
+ w = x_parent->left;
+ }
+ w->setColor(x_parent->color());
+ x_parent->setColor(SparseArrayNode::Black);
+ if (w->left)
+ w->left->setColor(SparseArrayNode::Black);
+ rotateRight(x_parent);
+ break;
+ }
+ }
+ }
+ if (x)
+ x->setColor(SparseArrayNode::Black);
+ }
+ free(y);
+ --numEntries;
+}
+
+void SparseArray::recalcMostLeftNode()
+{
+ mostLeftNode = &header;
+ while (mostLeftNode->left)
+ mostLeftNode = mostLeftNode->left;
+}
+
+static inline int qMapAlignmentThreshold()
+{
+ // malloc on 32-bit platforms should return pointers that are 8-byte
+ // aligned or more while on 64-bit platforms they should be 16-byte aligned
+ // or more
+ return 2 * sizeof(void*);
+}
+
+static inline void *qMapAllocate(int alloc, int alignment)
+{
+ return alignment > qMapAlignmentThreshold()
+ ? qMallocAligned(alloc, alignment)
+ : ::malloc(alloc);
+}
+
+static inline void qMapDeallocate(SparseArrayNode *node, int alignment)
+{
+ if (alignment > qMapAlignmentThreshold())
+ qFreeAligned(node);
+ else
+ ::free(node);
+}
+
+SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left)
+{
+ SparseArrayNode *node = static_cast<SparseArrayNode *>(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode)));
+ Q_CHECK_PTR(node);
+
+ node->p = (quintptr)parent;
+ node->left = 0;
+ node->right = 0;
+ node->size_left = sl;
+ node->value = UINT_MAX;
+ ++numEntries;
+
+ if (parent) {
+ if (left) {
+ parent->left = node;
+ if (parent == mostLeftNode)
+ mostLeftNode = node;
+ } else {
+ parent->right = node;
+ }
+ node->setParent(parent);
+ rebalance(node);
+ }
+ return node;
+}
+
+void SparseArray::freeTree(SparseArrayNode *root, int alignment)
+{
+ if (root->left)
+ freeTree(root->left, alignment);
+ if (root->right)
+ freeTree(root->right, alignment);
+ qMapDeallocate(root, alignment);
+}
+
+SparseArray::SparseArray()
+ : numEntries(0)
+{
+ header.p = 0;
+ header.left = 0;
+ header.right = 0;
+ mostLeftNode = &header;
+}
+
+SparseArray::SparseArray(const SparseArray &other)
+{
+ header.p = 0;
+ header.right = 0;
+ if (other.header.left) {
+ header.left = other.header.left->copy(this);
+ header.left->setParent(&header);
+ recalcMostLeftNode();
+ }
+}
+
+SparseArrayNode *SparseArray::insert(uint akey)
+{
+ SparseArrayNode *n = root();
+ SparseArrayNode *y = end();
+ bool left = true;
+ uint s = akey;
+ while (n) {
+ y = n;
+ if (s == n->size_left) {
+ return n;
+ } else if (s < n->size_left) {
+ left = true;
+ n = n->left;
+ } else {
+ left = false;
+ s -= n->size_left;
+ n = n->right;
+ }
+ }
+
+ return createNode(s, y, left);
+}
diff --git a/src/qml/qml/v4/qv4sparsearray_p.h b/src/qml/qml/v4/qv4sparsearray_p.h
new file mode 100644
index 0000000000..384d2ef045
--- /dev/null
+++ b/src/qml/qml/v4/qv4sparsearray_p.h
@@ -0,0 +1,367 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ARRAY_H
+#define QV4ARRAY_H
+
+#include "qv4global_p.h"
+#include <QtCore/qmap.h>
+#include "qv4value_p.h"
+#include "qv4property_p.h"
+#include <assert.h>
+
+#ifdef Q_MAP_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+#include <new>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct SparseArray;
+
+class ArrayElementLessThan
+{
+public:
+ inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn)
+ : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {}
+
+ bool operator()(const Property &v1, const Property &v2) const;
+
+private:
+ ExecutionContext *m_context;
+ Object *thisObject;
+ Value m_comparefn;
+};
+
+
+struct SparseArrayNode
+{
+ quintptr p;
+ SparseArrayNode *left;
+ SparseArrayNode *right;
+ uint size_left;
+ uint value;
+
+ enum Color { Red = 0, Black = 1 };
+ enum { Mask = 3 }; // reserve the second bit as well
+
+ const SparseArrayNode *nextNode() const;
+ SparseArrayNode *nextNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->nextNode()); }
+ const SparseArrayNode *previousNode() const;
+ SparseArrayNode *previousNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->previousNode()); }
+
+ Color color() const { return Color(p & 1); }
+ void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; }
+ SparseArrayNode *parent() const { return reinterpret_cast<SparseArrayNode *>(p & ~Mask); }
+ void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); }
+
+ uint key() const {
+ uint k = size_left;
+ const SparseArrayNode *n = this;
+ while (SparseArrayNode *p = n->parent()) {
+ if (p && p->right == n)
+ k += p->size_left;
+ n = p;
+ }
+ return k;
+ }
+
+ SparseArrayNode *copy(SparseArray *d) const;
+
+ SparseArrayNode *lowerBound(uint key);
+ SparseArrayNode *upperBound(uint key);
+};
+
+
+inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey)
+{
+ SparseArrayNode *n = this;
+ SparseArrayNode *last = 0;
+ while (n) {
+ if (akey <= n->size_left) {
+ last = n;
+ n = n->left;
+ } else {
+ akey -= n->size_left;
+ n = n->right;
+ }
+ }
+ return last;
+}
+
+
+inline SparseArrayNode *SparseArrayNode::upperBound(uint akey)
+{
+ SparseArrayNode *n = this;
+ SparseArrayNode *last = 0;
+ while (n) {
+ if (akey < n->size_left) {
+ last = n;
+ n = n->left;
+ } else {
+ akey -= n->size_left;
+ n = n->right;
+ }
+ }
+ return last;
+}
+
+
+
+struct Q_QML_EXPORT SparseArray
+{
+ SparseArray();
+ ~SparseArray() {
+ if (root())
+ freeTree(header.left, Q_ALIGNOF(SparseArrayNode));
+ }
+
+ SparseArray(const SparseArray &other);
+private:
+ SparseArray &operator=(const SparseArray &other);
+
+ int numEntries;
+ SparseArrayNode header;
+ SparseArrayNode *mostLeftNode;
+
+ void rotateLeft(SparseArrayNode *x);
+ void rotateRight(SparseArrayNode *x);
+ void rebalance(SparseArrayNode *x);
+ void recalcMostLeftNode();
+
+ SparseArrayNode *root() const { return header.left; }
+
+ void deleteNode(SparseArrayNode *z);
+
+
+public:
+ SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left);
+ void freeTree(SparseArrayNode *root, int alignment);
+
+ SparseArrayNode *findNode(uint akey) const;
+
+ uint pop_front();
+ void push_front(uint at);
+ uint pop_back(uint len);
+ void push_back(uint at, uint len);
+
+ QList<int> keys() const;
+
+ const SparseArrayNode *end() const { return &header; }
+ SparseArrayNode *end() { return &header; }
+ const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); }
+ SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); }
+
+ SparseArrayNode *erase(SparseArrayNode *n);
+
+ SparseArrayNode *lowerBound(uint key);
+ const SparseArrayNode *lowerBound(uint key) const;
+ SparseArrayNode *upperBound(uint key);
+ const SparseArrayNode *upperBound(uint key) const;
+ SparseArrayNode *insert(uint akey);
+
+ // STL compatibility
+ typedef uint key_type;
+ typedef int mapped_type;
+ typedef qptrdiff difference_type;
+ typedef int size_type;
+
+#ifdef Q_MAP_DEBUG
+ void dump() const;
+#endif
+};
+
+inline SparseArrayNode *SparseArray::findNode(uint akey) const
+{
+ SparseArrayNode *n = root();
+
+ while (n) {
+ if (akey == n->size_left) {
+ return n;
+ } else if (akey < n->size_left) {
+ n = n->left;
+ } else {
+ akey -= n->size_left;
+ n = n->right;
+ }
+ }
+
+ return 0;
+}
+
+inline uint SparseArray::pop_front()
+{
+ uint idx = UINT_MAX ;
+
+ SparseArrayNode *n = findNode(0);
+ if (n) {
+ idx = n->value;
+ deleteNode(n);
+ // adjust all size_left indices on the path to leftmost item by 1
+ SparseArrayNode *n = root();
+ while (n) {
+ n->size_left -= 1;
+ n = n->left;
+ }
+ }
+ return idx;
+}
+
+inline void SparseArray::push_front(uint value)
+{
+ // adjust all size_left indices on the path to leftmost item by 1
+ SparseArrayNode *n = root();
+ while (n) {
+ n->size_left += 1;
+ n = n->left;
+ }
+ n = insert(0);
+ n->value = value;
+}
+
+inline uint SparseArray::pop_back(uint len)
+{
+ uint idx = UINT_MAX;
+ if (!len)
+ return idx;
+
+ SparseArrayNode *n = findNode(len - 1);
+ if (n) {
+ idx = n->value;
+ deleteNode(n);
+ }
+ return idx;
+}
+
+inline void SparseArray::push_back(uint index, uint len)
+{
+ SparseArrayNode *n = insert(len);
+ n->value = index;
+}
+
+#ifdef Q_MAP_DEBUG
+
+void SparseArray::dump() const
+{
+ const_iterator it = begin();
+ qDebug() << "map dump:";
+ while (it != end()) {
+ const SparseArrayNode *n = it.i;
+ int depth = 0;
+ while (n && n != root()) {
+ ++depth;
+ n = n->parent();
+ }
+ QByteArray space(4*depth, ' ');
+ qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right
+ << it.key() << it.value();
+ ++it;
+ }
+ qDebug() << "---------";
+}
+#endif
+
+
+inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n)
+{
+ if (n == end())
+ return n;
+
+ SparseArrayNode *next = n->nextNode();
+ deleteNode(n);
+ return next;
+}
+
+inline QList<int> SparseArray::keys() const
+{
+ QList<int> res;
+ res.reserve(numEntries);
+ SparseArrayNode *n = mostLeftNode;
+ while (n != end()) {
+ res.append(n->key());
+ n = n->nextNode();
+ }
+ return res;
+}
+
+inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const
+{
+ const SparseArrayNode *lb = root()->lowerBound(akey);
+ if (!lb)
+ lb = end();
+ return lb;
+}
+
+
+inline SparseArrayNode *SparseArray::lowerBound(uint akey)
+{
+ SparseArrayNode *lb = root()->lowerBound(akey);
+ if (!lb)
+ lb = end();
+ return lb;
+}
+
+
+inline const SparseArrayNode *SparseArray::upperBound(uint akey) const
+{
+ const SparseArrayNode *ub = root()->upperBound(akey);
+ if (!ub)
+ ub = end();
+ return ub;
+}
+
+
+inline SparseArrayNode *SparseArray::upperBound(uint akey)
+{
+ SparseArrayNode *ub = root()->upperBound(akey);
+ if (!ub)
+ ub = end();
+ return ub;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QMAP_H
diff --git a/src/qml/qml/v4/qv4ssa.cpp b/src/qml/qml/v4/qv4ssa.cpp
new file mode 100644
index 0000000000..a139ed9fff
--- /dev/null
+++ b/src/qml/qml/v4/qv4ssa.cpp
@@ -0,0 +1,2122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4ssa_p.h"
+#include "qv4util_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+#include <QtCore/QBuffer>
+#include <QtCore/QLinkedList>
+#include <QtCore/QStack>
+#include <qv4runtime_p.h>
+#include <qv4context_p.h>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+#ifdef CONST
+#undef CONST
+#endif
+
+#define QV4_NO_LIVENESS
+#undef SHOW_SSA
+
+QT_USE_NAMESPACE
+
+using namespace QQmlJS;
+using namespace V4IR;
+
+namespace {
+
+QTextStream qout(stdout, QIODevice::WriteOnly);
+
+void showMeTheCode(Function *function)
+{
+ static bool showCode = !qgetenv("SHOW_CODE").isNull();
+ if (showCode) {
+ QVector<Stmt *> code;
+ QHash<Stmt *, BasicBlock *> leader;
+
+ foreach (BasicBlock *block, function->basicBlocks) {
+ if (block->statements.isEmpty())
+ continue;
+ leader.insert(block->statements.first(), block);
+ foreach (Stmt *s, block->statements) {
+ code.append(s);
+ }
+ }
+
+ QString name;
+ if (function->name && !function->name->isEmpty())
+ name = *function->name;
+ else
+ name.sprintf("%p", function);
+
+ qout << "function " << name << "(";
+ for (int i = 0; i < function->formals.size(); ++i) {
+ if (i != 0)
+ qout << ", ";
+ qout << *function->formals.at(i);
+ }
+ qout << ")" << endl
+ << "{" << endl;
+
+ foreach (const QString *local, function->locals) {
+ qout << " var " << *local << ';' << endl;
+ }
+
+ for (int i = 0; i < code.size(); ++i) {
+ Stmt *s = code.at(i);
+
+ if (BasicBlock *bb = leader.value(s)) {
+ qout << endl;
+ QByteArray str;
+ str.append('L');
+ str.append(QByteArray::number(bb->index));
+ str.append(':');
+ for (int i = 66 - str.length(); i; --i)
+ str.append(' ');
+ qout << str;
+ qout << "// predecessor blocks:";
+ foreach (BasicBlock *in, bb->in)
+ qout << " L" << in->index;
+ if (bb->in.isEmpty())
+ qout << "(none)";
+ if (BasicBlock *container = bb->containingGroup())
+ qout << "; container block: L" << container->index;
+ if (bb->isGroupStart())
+ qout << "; group start";
+ qout << endl;
+ }
+ Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0;
+// if (n && s->asJump() && s->asJump()->target == leader.value(n)) {
+// continue;
+// }
+
+ QByteArray str;
+ QBuffer buf(&str);
+ buf.open(QIODevice::WriteOnly);
+ QTextStream out(&buf);
+ if (s->id > 0)
+ out << s->id << ": ";
+ s->dump(out, Stmt::MIR);
+ out.flush();
+
+ if (s->location.isValid())
+ qout << " // line: " << s->location.startLine << " column: " << s->location.startColumn << endl;
+
+#ifndef QV4_NO_LIVENESS
+ for (int i = 60 - str.size(); i >= 0; --i)
+ str.append(' ');
+
+ qout << " " << str;
+
+ // if (! s->uses.isEmpty()) {
+ // qout << " // uses:";
+ // foreach (unsigned use, s->uses) {
+ // qout << " %" << use;
+ // }
+ // }
+
+ // if (! s->defs.isEmpty()) {
+ // qout << " // defs:";
+ // foreach (unsigned def, s->defs) {
+ // qout << " %" << def;
+ // }
+ // }
+
+# if 0
+ if (! s->d->liveIn.isEmpty()) {
+ qout << " // lives in:";
+ for (int i = 0; i < s->d->liveIn.size(); ++i) {
+ if (s->d->liveIn.testBit(i))
+ qout << " %" << i;
+ }
+ }
+# else
+ if (! s->d->liveOut.isEmpty()) {
+ qout << " // lives out:";
+ for (int i = 0; i < s->d->liveOut.size(); ++i) {
+ if (s->d->liveOut.testBit(i))
+ qout << " %" << i;
+ }
+ }
+# endif
+#else
+ qout << " " << str;
+#endif
+
+ qout << endl;
+
+ if (n && s->asCJump() /*&& s->asCJump()->iffalse != leader.value(n)*/) {
+ qout << " else goto L" << s->asCJump()->iffalse->index << ";" << endl;
+ }
+ }
+
+ qout << "}" << endl
+ << endl;
+ }
+}
+
+class DominatorTree {
+ int N;
+ QHash<BasicBlock *, int> dfnum;
+ QVector<BasicBlock *> vertex;
+ QHash<BasicBlock *, BasicBlock *> parent;
+ QHash<BasicBlock *, BasicBlock *> ancestor;
+ QHash<BasicBlock *, BasicBlock *> best;
+ QHash<BasicBlock *, BasicBlock *> semi;
+ QHash<BasicBlock *, BasicBlock *> idom;
+ QHash<BasicBlock *, BasicBlock *> samedom;
+ QHash<BasicBlock *, QSet<BasicBlock *> > bucket;
+
+ void DFS(BasicBlock *p, BasicBlock *n) {
+ if (dfnum[n] == 0) {
+ dfnum[n] = N;
+ vertex[N] = n;
+ parent[n] = p;
+ ++N;
+ foreach (BasicBlock *w, n->out)
+ DFS(n, w);
+ }
+ }
+
+ BasicBlock *ancestorWithLowestSemi(BasicBlock *v) {
+ BasicBlock *a = ancestor[v];
+ if (ancestor[a]) {
+ BasicBlock *b = ancestorWithLowestSemi(a);
+ ancestor[v] = ancestor[a];
+ if (dfnum[semi[b]] < dfnum[semi[best[v]]])
+ best[v] = b;
+ }
+ return best[v];
+ }
+
+ void link(BasicBlock *p, BasicBlock *n) {
+ ancestor[n] = p;
+ best[n] = n;
+ }
+
+ void calculateIDoms(const QVector<BasicBlock *> &nodes) {
+ Q_ASSERT(nodes.first()->in.isEmpty());
+ vertex.resize(nodes.size());
+ foreach (BasicBlock *n, nodes) {
+ dfnum[n] = 0;
+ semi[n] = 0;
+ ancestor[n] = 0;
+ idom[n] = 0;
+ samedom[n] = 0;
+ }
+
+ DFS(0, nodes.first());
+ Q_ASSERT(N == nodes.size()); // fails with unreachable nodes...
+
+ for (int i = N - 1; i > 0; --i) {
+ BasicBlock *n = vertex[i];
+ BasicBlock *p = parent[n];
+ BasicBlock *s = p;
+
+ foreach (BasicBlock *v, n->in) {
+ BasicBlock *ss;
+ if (dfnum[v] <= dfnum[n])
+ ss = v;
+ else
+ ss = semi[ancestorWithLowestSemi(v)];
+ if (dfnum[ss] < dfnum[s])
+ s = ss;
+ }
+ semi[n] = s;
+ bucket[s].insert(n);
+ link(p, n);
+ foreach (BasicBlock *v, bucket[p]) {
+ BasicBlock *y = ancestorWithLowestSemi(v);
+ Q_ASSERT(semi[y] == p);
+ if (semi[y] == semi[v])
+ idom[v] = p;
+ else
+ samedom[v] = y;
+ }
+ bucket[p].clear();
+ }
+ for (int i = 1; i < N; ++i) {
+ BasicBlock *n = vertex[i];
+ Q_ASSERT(ancestor[n] && ((semi[n] && dfnum[ancestor[n]] <= dfnum[semi[n]]) || semi[n] == n));
+ Q_ASSERT(bucket[n].isEmpty());
+ if (BasicBlock *sdn = samedom[n])
+ idom[n] = idom[sdn];
+ }
+
+#ifdef SHOW_SSA
+ qout << "Immediate dominators:" << endl;
+ foreach (BasicBlock *to, nodes) {
+ qout << '\t';
+ if (BasicBlock *from = idom.value(to))
+ qout << from->index;
+ else
+ qout << "(none)";
+ qout << " -> " << to->index << endl;
+ }
+#endif // SHOW_SSA
+ }
+
+ bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
+ for (BasicBlock *it = dominated; it; it = idom[it]) {
+ if (it == dominator)
+ return true;
+ }
+
+ return false;
+ }
+
+ void computeDF(BasicBlock *n) {
+ if (DF.contains(n))
+ return; // TODO: verify this!
+
+ QSet<BasicBlock *> S;
+ foreach (BasicBlock *y, n->out)
+ if (idom[y] != n)
+ S.insert(y);
+
+ /*
+ * foreach child c of n in the dominator tree
+ * computeDF[c]
+ * foreach element w of DF[c]
+ * if n does not dominate w or if n = w
+ * S.insert(w)
+ * DF[n] = S;
+ */
+ foreach (BasicBlock *c, children[n]) {
+ computeDF(c);
+ foreach (BasicBlock *w, DF[c])
+ if (!dominates(n, w) || n == w)
+ S.insert(w);
+ }
+ DF[n] = S;
+
+#ifdef SHOW_SSA
+ qout << "\tDF[" << n->index << "]: {";
+ QList<BasicBlock *> SList = S.values();
+ for (int i = 0; i < SList.size(); ++i) {
+ if (i > 0)
+ qout << ", ";
+ qout << SList[i]->index;
+ }
+ qout << "}" << endl;
+#endif // SHOW_SSA
+#ifndef QT_NO_DEBUG
+ foreach (BasicBlock *fBlock, S) {
+ Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
+ bool hasDominatedSucc = false;
+ foreach (BasicBlock *succ, fBlock->in)
+ if (dominates(n, succ))
+ hasDominatedSucc = true;
+ if (!hasDominatedSucc) {
+ qout << fBlock->index << " in DF[" << n->index << "] has no dominated predecessors" << endl;
+ }
+ Q_ASSERT(hasDominatedSucc);
+ }
+#endif // !QT_NO_DEBUG
+ }
+
+ QHash<BasicBlock *, QSet<BasicBlock *> > children;
+ QHash<BasicBlock *, QSet<BasicBlock *> > DF;
+
+public:
+ DominatorTree(const QVector<BasicBlock *> &nodes)
+ : N(0)
+ {
+ calculateIDoms(nodes);
+
+ // compute children of n
+ foreach (BasicBlock *n, nodes)
+ children[idom[n]].insert(n);
+
+#ifdef SHOW_SSA
+ qout << "Dominator Frontiers:" << endl;
+#endif // SHOW_SSA
+ foreach (BasicBlock *n, nodes)
+ computeDF(n);
+ }
+
+ QSet<BasicBlock *> operator[](BasicBlock *n) const {
+ return DF[n];
+ }
+
+ BasicBlock *immediateDominator(BasicBlock *bb) const {
+ return idom[bb];
+ }
+};
+
+class VariableCollector: public StmtVisitor, ExprVisitor {
+ QHash<Temp, QSet<BasicBlock *> > _defsites;
+ QHash<BasicBlock *, QSet<Temp> > A_orig;
+ QSet<Temp> nonLocals;
+ QSet<Temp> killed;
+
+ BasicBlock *currentBB;
+ const bool variablesCanEscape;
+ bool isCollectable(Temp *t) const
+ {
+ switch (t->kind) {
+ case Temp::Formal:
+ case Temp::ScopedFormal:
+ case Temp::ScopedLocal:
+ return false;
+ case Temp::Local:
+ return !variablesCanEscape;
+ case Temp::VirtualRegister:
+ return true;
+ default:
+ // PhysicalRegister and StackSlot can only get inserted later.
+ Q_ASSERT(!"Invalid temp kind!");
+ return false;
+ }
+ }
+
+public:
+ VariableCollector(Function *function)
+ : variablesCanEscape(function->variablesCanEscape())
+ {
+#ifdef SHOW_SSA
+ qout << "Variables collected:" << endl;
+#endif // SHOW_SSA
+
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ currentBB = bb;
+ killed.clear();
+ killed.reserve(bb->statements.size() / 2);
+ foreach (Stmt *s, bb->statements) {
+ s->accept(this);
+ }
+ }
+
+#ifdef SHOW_SSA
+ qout << "Non-locals:" << endl;
+ foreach (const Temp &nonLocal, nonLocals) {
+ qout << "\t";
+ nonLocal.dump(qout);
+ qout << endl;
+ }
+
+ qout << "end collected variables." << endl;
+#endif // SHOW_SSA
+ }
+
+ QList<Temp> vars() const {
+ return _defsites.keys();
+ }
+
+ QSet<BasicBlock *> defsite(const Temp &n) const {
+ return _defsites[n];
+ }
+
+ QSet<Temp> inBlock(BasicBlock *n) const {
+ return A_orig[n];
+ }
+
+ bool isNonLocal(const Temp &var) const { return nonLocals.contains(var); }
+
+protected:
+ virtual void visitPhi(Phi *) {};
+ virtual void visitConvert(Convert *e) { e->expr->accept(this); };
+
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitClosure(Closure *) {}
+ virtual void visitUnop(Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(Member *e) { e->base->accept(this); }
+ virtual void visitExp(Exp *s) { s->expr->accept(this); }
+ virtual void visitJump(Jump *) {}
+ virtual void visitCJump(CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(Ret *s) { s->expr->accept(this); }
+ virtual void visitTry(Try *) { // ### TODO
+ }
+
+ virtual void visitCall(Call *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitNew(New *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitMove(Move *s) {
+ s->source->accept(this);
+
+ if (Temp *t = s->target->asTemp()) {
+ if (isCollectable(t)) {
+#ifdef SHOW_SSA
+ qout << '\t';
+ t->dump(qout);
+ qout << " -> L" << currentBB->index << endl;
+#endif // SHOW_SSA
+
+ _defsites[*t].insert(currentBB);
+ A_orig[currentBB].insert(*t);
+
+ // For semi-pruned SSA:
+ killed.insert(*t);
+ }
+ }
+ }
+
+ virtual void visitTemp(Temp *t)
+ {
+ if (isCollectable(t))
+ if (!killed.contains(*t))
+ nonLocals.insert(*t);
+ }
+};
+
+void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) {
+#if defined(SHOW_SSA)
+ qout << "-> inserted phi node for variable ";
+ a.dump(qout);
+ qout << " in block " << y->index << endl;
+#endif
+
+ Phi *phiNode = f->New<Phi>();
+ phiNode->targetTemp = f->New<Temp>();
+ phiNode->targetTemp->init(a.kind, a.index, 0);
+ y->statements.prepend(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, 0);
+ phiNode->incoming[i] = t;
+ }
+}
+
+class VariableRenamer: public StmtVisitor, public ExprVisitor
+{
+ Function *function;
+ QHash<Temp, QStack<unsigned> > stack;
+ QSet<BasicBlock *> seen;
+
+ QHash<Temp, unsigned> defCounts;
+
+ const bool variablesCanEscape;
+ bool isRenamable(Temp *t) const
+ {
+ switch (t->kind) {
+ case Temp::Formal:
+ case Temp::ScopedFormal:
+ case Temp::ScopedLocal:
+ return false;
+ case Temp::Local:
+ return !variablesCanEscape;
+ case Temp::VirtualRegister:
+ return true;
+ default:
+ Q_ASSERT(!"Invalid temp kind!");
+ return false;
+ }
+ }
+ int nextFreeTemp() {
+ const int next = function->tempCount++;
+// qDebug()<<"Next free temp:"<<next;
+ return next;
+ }
+
+ /*
+
+ Initialization:
+ for each variable a
+ count[a] = 0;
+ stack[a] = empty;
+ push 0 onto stack
+
+ Rename(n) =
+ for each statement S in block n [1]
+ if S not in a phi-function
+ for each use of some variable x in S
+ i = top(stack[x])
+ replace the use of x with x_i in S
+ for each definition of some variable a in S
+ count[a] = count[a] + 1
+ i = count[a]
+ push i onto stack[a]
+ replace definition of a with definition of a_i in S
+ for each successor Y of block n [2]
+ 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 = top(stack[a])
+ replace the j-th operand with a_i
+ for each child X of n [3]
+ Rename(X)
+ for each statement S in block n [4]
+ for each definition of some variable a in S
+ pop stack[a]
+
+ */
+
+public:
+ VariableRenamer(Function *f)
+ : function(f)
+ , variablesCanEscape(f->variablesCanEscape())
+ {
+ if (!variablesCanEscape) {
+ Temp t;
+ t.init(Temp::Local, 0, 0);
+ for (int i = 0, ei = f->locals.size(); i != ei; ++i) {
+ t.index = i;
+ stack[t].push(nextFreeTemp());
+ }
+ }
+
+ Temp t;
+ t.init(Temp::VirtualRegister, 0, 0);
+ for (int i = 0, ei = f->tempCount; i != ei; ++i) {
+ t.index = i;
+ stack[t].push(i);
+ }
+ }
+
+ void run() {
+ foreach (BasicBlock *n, function->basicBlocks)
+ rename(n);
+
+#ifdef SHOW_SSA
+// qout << "Temp to local mapping:" << endl;
+// foreach (int key, tempMapping.keys())
+// qout << '\t' << key << " -> " << tempMapping[key] << endl;
+#endif
+ }
+
+ void rename(BasicBlock *n) {
+ if (seen.contains(n))
+ return;
+ seen.insert(n);
+// qDebug() << "I: L"<<n->index;
+
+ // [1]:
+ foreach (Stmt *s, n->statements)
+ s->accept(this);
+
+ QHash<Temp, unsigned> dc = defCounts;
+ defCounts.clear();
+
+ // [2]:
+ foreach (BasicBlock *Y, n->out) {
+ const int j = Y->in.indexOf(n);
+ Q_ASSERT(j >= 0 && j < Y->in.size());
+ foreach (Stmt *s, Y->statements) {
+ if (Phi *phi = s->asPhi()) {
+ Temp *t = phi->incoming[j]->asTemp();
+ unsigned newTmp = stack[*t].top();
+// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index;
+ t->index = newTmp;
+ t->kind = Temp::VirtualRegister;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // [3]:
+ foreach (BasicBlock *X, n->out)
+ rename(X);
+
+ // [4]:
+ for (QHash<Temp, unsigned>::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) {
+// qDebug()<<i.key() <<" -> " << i.value();
+ for (unsigned j = 0, ej = i.value(); j < ej; ++j)
+ stack[i.key()].pop();
+ }
+ }
+
+protected:
+ virtual void visitTemp(Temp *e) { // only called for uses, not defs
+ if (isRenamable(e)) {
+// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top();
+ e->index = stack[*e].top();
+ e->kind = Temp::VirtualRegister;
+ }
+ }
+
+ virtual void visitMove(Move *s) {
+ // uses:
+ s->source->accept(this);
+
+ // defs:
+ if (Temp *t = s->target->asTemp())
+ renameTemp(t);
+ else
+ s->target->accept(this);
+ }
+
+ void renameTemp(Temp *t) {
+ if (isRenamable(t)) {
+ defCounts[*t] = defCounts.value(*t, 0) + 1;
+ const int newIdx = nextFreeTemp();
+ stack[*t].push(newIdx);
+// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx;
+ t->kind = Temp::VirtualRegister;
+ t->index = newIdx;
+ }
+ }
+
+ virtual void visitConvert(Convert *e) { e->expr->accept(this); }
+ virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); }
+
+ virtual void visitExp(Exp *s) { s->expr->accept(this); }
+
+ virtual void visitJump(Jump *) {}
+ virtual void visitCJump(CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(Ret *s) { s->expr->accept(this); }
+ virtual void visitTry(Try *s) { /* this should never happen */ }
+
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitClosure(Closure *) {}
+ virtual void visitUnop(Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitCall(Call *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitNew(New *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitSubscript(Subscript *e) {
+ e->base->accept(this);
+ e->index->accept(this);
+ }
+
+ virtual void visitMember(Member *e) {
+ e->base->accept(this);
+ }
+};
+
+void convertToSSA(Function *function, const DominatorTree &df)
+{
+#ifdef SHOW_SSA
+ qout << "Converting function ";
+ if (function->name)
+ qout << *function->name;
+ else
+ qout << "<no name>";
+ qout << " to SSA..." << endl;
+#endif // SHOW_SSA
+
+ // Collect all applicable variables:
+ VariableCollector variables(function);
+
+ // Place phi functions:
+ QHash<BasicBlock *, QSet<Temp> > A_phi;
+ foreach (Temp a, variables.vars()) {
+ if (!variables.isNonLocal(a))
+ continue; // for semi-pruned SSA
+
+ QList<BasicBlock *> W = QList<BasicBlock *>::fromSet(variables.defsite(a));
+ while (!W.isEmpty()) {
+ BasicBlock *n = W.first();
+ W.removeFirst();
+ foreach (BasicBlock *y, df[n]) {
+ if (!A_phi[y].contains(a)) {
+ insertPhiNode(a, y, function);
+ A_phi[y].insert(a);
+ if (!variables.inBlock(y).contains(a))
+ W.append(y);
+ }
+ }
+ }
+ }
+ showMeTheCode(function);
+
+ // Rename variables:
+ VariableRenamer(function).run();
+}
+
+class DefUsesCalculator: public StmtVisitor, public ExprVisitor {
+public:
+ struct DefUse {
+ DefUse()
+ : defStmt(0)
+ , blockOfStatement(0)
+ {}
+ Stmt *defStmt;
+ BasicBlock *blockOfStatement;
+ QList<Stmt *> uses;
+ };
+
+private:
+ const bool _variablesCanEscape;
+ QHash<Temp, DefUse> _defUses;
+ QHash<Stmt *, QList<Temp> > _usesPerStatement;
+
+ BasicBlock *_block;
+ Stmt *_stmt;
+
+ bool isCollectible(Temp *t) const {
+ switch (t->kind) {
+ case Temp::Formal:
+ case Temp::ScopedFormal:
+ case Temp::ScopedLocal:
+ return false;
+ case Temp::Local:
+ return !_variablesCanEscape;
+ case Temp::VirtualRegister:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+ }
+
+ void addUse(Temp *t) {
+ Q_ASSERT(t);
+ if (!isCollectible(t))
+ return;
+
+ _defUses[*t].uses.append(_stmt);
+ _usesPerStatement[_stmt].append(*t);
+ }
+
+ void addDef(Temp *t) {
+ if (!isCollectible(t))
+ return;
+
+ Q_ASSERT(!_defUses.contains(*t) || _defUses.value(*t).defStmt == 0 || _defUses.value(*t).defStmt == _stmt);
+
+ DefUse &defUse = _defUses[*t];
+ defUse.defStmt = _stmt;
+ defUse.blockOfStatement = _block;
+ }
+
+public:
+ DefUsesCalculator(Function *function)
+ : _variablesCanEscape(function->variablesCanEscape())
+ {
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ _block = bb;
+ foreach (Stmt *stmt, bb->statements) {
+ _stmt = stmt;
+ stmt->accept(this);
+ }
+ }
+
+ QMutableHashIterator<Temp, DefUse> it(_defUses);
+ while (it.hasNext()) {
+ it.next();
+ if (!it.value().defStmt)
+ it.remove();
+ }
+ }
+
+ QList<Temp> defs() const {
+ return _defUses.keys();
+ }
+
+ void removeDef(const Temp &var) {
+ _defUses.remove(var);
+ }
+
+ void addUses(const Temp &variable, const QList<Stmt *> &newUses)
+ { _defUses[variable].uses.append(newUses); }
+
+ int useCount(const Temp &variable) const
+ { return _defUses[variable].uses.size(); }
+
+ Stmt *defStmt(const Temp &variable) const
+ { return _defUses[variable].defStmt; }
+
+ BasicBlock *defStmtBlock(const Temp &variable) const
+ { return _defUses[variable].blockOfStatement; }
+
+ void removeUse(Stmt *usingStmt, const Temp &var)
+ { _defUses[var].uses.removeAll(usingStmt); }
+
+ QList<Temp> usedVars(Stmt *s) const
+ { return _usesPerStatement[s]; }
+
+ QList<Stmt *> uses(const Temp &var) const
+ { return _defUses[var].uses; }
+
+ void dump() const
+ {
+ foreach (const Temp &var, _defUses.keys()) {
+ const DefUse &du = _defUses[var];
+ var.dump(qout);
+ qout<<" -> defined in block "<<du.blockOfStatement->index<<", statement: ";
+ du.defStmt->dump(qout);
+ qout<<endl<<" uses:"<<endl;
+ foreach (Stmt *s, du.uses) {
+ qout<<" ";s->dump(qout);qout<<endl;
+ }
+ }
+ }
+
+protected:
+ virtual void visitExp(Exp *s) { s->expr->accept(this); }
+ virtual void visitJump(Jump *) {}
+ virtual void visitCJump(CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(Ret *s) { s->expr->accept(this); }
+ virtual void visitTry(Try *) {}
+
+ virtual void visitPhi(Phi *s) {
+ addDef(s->targetTemp);
+ foreach (Expr *e, s->incoming)
+ addUse(e->asTemp());
+ }
+
+ virtual void visitMove(Move *s) {
+ if (Temp *t = s->target->asTemp())
+ addDef(t);
+ else
+ s->target->accept(this);
+
+ s->source->accept(this);
+ }
+
+ virtual void visitTemp(Temp *e) { addUse(e); }
+
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitClosure(Closure *) {}
+ virtual void visitConvert(Convert *e) { e->expr->accept(this); }
+ virtual void visitUnop(Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(Member *e) { e->base->accept(this); }
+ virtual void visitCall(Call *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitNew(New *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+};
+
+bool hasPhiOnlyUses(Phi *phi, const DefUsesCalculator &defUses, QSet<Phi *> &collectedPhis)
+{
+ collectedPhis.insert(phi);
+ foreach (Stmt *use, defUses.uses(*phi->targetTemp)) {
+ if (Phi *dependentPhi = use->asPhi()) {
+ if (!collectedPhis.contains(dependentPhi)) {
+ if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis))
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void cleanupPhis(DefUsesCalculator &defUses)
+{
+ QLinkedList<Phi *> phis;
+ foreach (const Temp &def, defUses.defs())
+ if (Phi *phi = defUses.defStmt(def)->asPhi())
+ phis.append(phi);
+
+ QSet<Phi *> toRemove;
+ while (!phis.isEmpty()) {
+ Phi *phi = phis.first();
+ phis.removeFirst();
+ if (toRemove.contains(phi))
+ continue;
+ QSet<Phi *> collectedPhis;
+ if (hasPhiOnlyUses(phi, defUses, collectedPhis))
+ toRemove.unite(collectedPhis);
+ }
+
+ foreach (Phi *phi, toRemove) {
+ Temp targetVar = *phi->targetTemp;
+
+ BasicBlock *bb = defUses.defStmtBlock(targetVar);
+ int idx = bb->statements.indexOf(phi);
+ bb->statements.remove(idx);
+
+ foreach (const Temp &usedVar, defUses.usedVars(phi))
+ defUses.removeUse(phi, usedVar);
+ defUses.removeDef(targetVar);
+ }
+}
+
+class DeadCodeElimination: public ExprVisitor {
+ const bool variablesCanEscape;
+ DefUsesCalculator &_defUses;
+ QVector<Temp> _worklist;
+
+public:
+ DeadCodeElimination(DefUsesCalculator &defUses, Function *function)
+ : variablesCanEscape(function->variablesCanEscape())
+ , _defUses(defUses)
+ {
+ _worklist = QVector<Temp>::fromList(_defUses.defs());
+ }
+
+ void run() {
+ while (!_worklist.isEmpty()) {
+ const Temp v = _worklist.first();
+ _worklist.removeFirst();
+
+ if (_defUses.useCount(v) == 0) {
+// qDebug()<<"-"<<v<<"has no uses...";
+ Stmt *s = _defUses.defStmt(v);
+ if (!s) {
+ _defUses.removeDef(v);
+ } else if (!hasSideEffect(s)) {
+#ifdef SHOW_SSA
+ qout<<"-- defining stmt for";
+ v.dump(qout);
+ qout<<"has no side effect"<<endl;
+#endif
+ QVector<Stmt *> &stmts = _defUses.defStmtBlock(v)->statements;
+ int idx = stmts.indexOf(s);
+ if (idx != -1)
+ stmts.remove(idx);
+ foreach (const Temp &usedVar, _defUses.usedVars(s)) {
+ _defUses.removeUse(s, usedVar);
+ _worklist.append(usedVar);
+ }
+ _defUses.removeDef(v);
+ }
+ }
+ }
+
+#ifdef SHOW_SSA
+ qout<<"******************* After dead-code elimination:";
+ _defUses.dump();
+#endif
+ }
+
+private:
+ bool _sideEffect;
+
+ bool hasSideEffect(Stmt *s) {
+ // TODO: check if this can be moved to IR building.
+ _sideEffect = false;
+ if (Move *move = s->asMove()) {
+ if (Temp *t = move->target->asTemp()) {
+ switch (t->kind) {
+ case Temp::Formal:
+ case Temp::ScopedFormal:
+ case Temp::ScopedLocal:
+ return true;
+ case Temp::Local:
+ if (variablesCanEscape)
+ return true;
+ else
+ break;
+ case Temp::VirtualRegister:
+ break;
+ default:
+ Q_ASSERT(!"Invalid temp kind!");
+ return true;
+ }
+ move->source->accept(this);
+ } else {
+ return true;
+ }
+ }
+ return _sideEffect;
+ }
+
+protected:
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *e) {
+ // 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 != QStringLiteral("this")))
+ _sideEffect = true;
+ }
+ virtual void visitTemp(Temp *e) {
+ }
+ virtual void visitClosure(Closure *) {}
+ virtual void visitConvert(Convert *e) {
+ // we do not have type information yet, so:
+ _sideEffect = true;
+ }
+
+ virtual void visitUnop(Unop *e) {
+ switch (e->op) {
+ case OpIncrement:
+ case OpDecrement:
+ _sideEffect = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!_sideEffect) e->expr->accept(this);
+ }
+ virtual void visitBinop(Binop *e) { if (!_sideEffect) e->left->accept(this); if (!_sideEffect) e->right->accept(this); }
+ virtual void visitSubscript(Subscript *e) {
+ // TODO: see if we can have subscript accesses without side effect
+ _sideEffect = true;
+ }
+ virtual void visitMember(Member *e) {
+ // TODO: see if we can have member accesses without side effect
+ _sideEffect = true;
+ }
+ virtual void visitCall(Call *e) {
+ _sideEffect = true; // TODO: there are built-in functions that have no side effect.
+ }
+ virtual void visitNew(New *e) {
+ _sideEffect = true; // TODO: there are built-in types that have no side effect.
+ }
+};
+
+class TypeInference: public StmtVisitor, public ExprVisitor {
+ bool _variablesCanEscape;
+ const DefUsesCalculator &_defUses;
+ QHash<Temp, int> _tempTypes;
+ QSet<Stmt *> _worklist;
+ struct TypingResult {
+ int type;
+ bool fullyTyped;
+
+ TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {}
+ explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {}
+ };
+ TypingResult _ty;
+
+public:
+ TypeInference(const DefUsesCalculator &defUses)
+ : _defUses(defUses)
+ , _ty(UnknownType)
+ {}
+
+ void run(Function *function) {
+ _variablesCanEscape = function->variablesCanEscape();
+
+ // TODO: the worklist handling looks a bit inefficient... check if there is something better
+ _worklist.clear();
+ for (int i = 0, ei = function->basicBlocks.size(); i != ei; ++i) {
+ BasicBlock *bb = function->basicBlocks[i];
+ if (i == 0 || !bb->in.isEmpty())
+ foreach (Stmt *s, bb->statements)
+ _worklist.insert(s);
+ }
+
+ while (!_worklist.isEmpty()) {
+ QList<Stmt *> worklist = _worklist.values();
+ _worklist.clear();
+ while (!worklist.isEmpty()) {
+ Stmt *s = worklist.first();
+ worklist.removeFirst();
+#if defined(SHOW_SSA)
+ qout<<"Typing stmt ";s->dump(qout);qout<<endl;
+#endif
+
+ if (!run(s)) {
+ _worklist.insert(s);
+#if defined(SHOW_SSA)
+ qout<<"Pushing back stmt: ";
+ s->dump(qout);qout<<endl;
+ } else {
+ qout<<"Finished: ";
+ s->dump(qout);qout<<endl;
+#endif
+ }
+ }
+ }
+ }
+
+private:
+ bool run(Stmt *s) {
+ TypingResult ty;
+ std::swap(_ty, ty);
+ s->accept(this);
+ std::swap(_ty, ty);
+ return ty.fullyTyped;
+ }
+
+ TypingResult run(Expr *e) {
+ TypingResult ty;
+ std::swap(_ty, ty);
+ e->accept(this);
+ std::swap(_ty, ty);
+
+ if (ty.type != UnknownType)
+ setType(e, ty.type);
+ return ty;
+ }
+
+ bool isAlwaysAnObject(Temp *t) {
+ switch (t->kind) {
+ case Temp::Formal:
+ case Temp::ScopedFormal:
+ case Temp::ScopedLocal:
+ return true;
+ case Temp::Local:
+ return _variablesCanEscape;
+ default:
+ return false;
+ }
+ }
+
+ void setType(Expr *e, int ty) {
+ if (Temp *t = e->asTemp()) {
+#if defined(SHOW_SSA)
+ qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <<t->index<< " to "<<typeName(Type(ty)) << " (" << ty << ")" << endl;
+#endif
+ if (isAlwaysAnObject(t)) {
+ e->type = ObjectType;
+ } else {
+ e->type = (Type) ty;
+
+ if (_tempTypes[*t] != ty) {
+ _tempTypes[*t] = ty;
+
+#if defined(SHOW_SSA)
+ foreach (Stmt *s, _defUses.uses(*t)) {
+ qout << "Pushing back dependent stmt: ";
+ s->dump(qout);
+ qout << endl;
+ }
+#endif
+
+ _worklist += QSet<Stmt *>::fromList(_defUses.uses(*t));
+ }
+ }
+ } else {
+ e->type = (Type) ty;
+ }
+ }
+
+protected:
+ virtual void visitConst(Const *e) { _ty = TypingResult(e->type); }
+ virtual void visitString(String *) { _ty = TypingResult(StringType); }
+ virtual void visitRegExp(RegExp *) { _ty = TypingResult(ObjectType); }
+ virtual void visitName(Name *) { _ty = TypingResult(ObjectType); }
+ virtual void visitTemp(Temp *e) {
+ if (isAlwaysAnObject(e))
+ _ty = TypingResult(ObjectType);
+ else
+ _ty = TypingResult(_tempTypes.value(*e, UnknownType));
+ setType(e, _ty.type);
+ }
+ virtual void visitClosure(Closure *) { _ty = TypingResult(ObjectType); } // TODO: VERIFY THIS!
+ virtual void visitConvert(Convert *e) {
+ _ty = run(e->expr);
+ }
+
+ virtual 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!");
+ default:
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ }
+ }
+
+ virtual 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 & StringType || rightTy.type & 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();
+ }
+ }
+
+ virtual 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 = ObjectType;
+ }
+ virtual 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 = ObjectType;
+ }
+ virtual void visitSubscript(Subscript *e) {
+ _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
+ _ty.type = ObjectType;
+ }
+
+ virtual void visitMember(Member *e) {
+ // TODO: for QML, try to do a static lookup
+ _ty = run(e->base);
+ _ty.type = ObjectType;
+ }
+
+ virtual void visitExp(Exp *s) { _ty = run(s->expr); }
+ virtual void visitMove(Move *s) {
+ TypingResult sourceTy = run(s->source);
+ Q_ASSERT(s->op == OpInvalid);
+ if (Temp *t = s->target->asTemp()) {
+ setType(t, sourceTy.type);
+ _ty = sourceTy;
+ return;
+ }
+
+ _ty = run(s->target);
+ _ty.fullyTyped &= sourceTy.fullyTyped;
+ }
+
+ virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); }
+ virtual void visitCJump(CJump *s) { _ty = run(s->cond); }
+ virtual void visitRet(Ret *s) { _ty = run(s->expr); }
+ virtual void visitTry(Try *s) { setType(s->exceptionVar, ObjectType); _ty = TypingResult(MissingType); }
+ virtual 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]);
+ _ty.type |= ty.type;
+ _ty.fullyTyped &= ty.fullyTyped;
+ }
+
+ // TODO: check & double check the next condition!
+ if (_ty.type & ObjectType || _ty.type & UndefinedType || _ty.type & NullType)
+ _ty.type = ObjectType;
+ else if (_ty.type & NumberType)
+ _ty.type = DoubleType;
+
+ setType(s->targetTemp, _ty.type);
+ }
+};
+
+class TypePropagation: public StmtVisitor, public ExprVisitor {
+ Type _ty;
+
+ void run(Expr *&e, Type requestedType = UnknownType) {
+ qSwap(_ty, requestedType);
+ e->accept(this);
+ qSwap(_ty, requestedType);
+
+ if (requestedType != UnknownType)
+ if (e->type != requestedType)
+ if (requestedType & NumberType) {
+// qDebug()<<"adding conversion from"<<typeName(e->type)<<"to"<<typeName(requestedType);
+ addConversion(e, requestedType);
+ }
+ }
+
+ 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() : _ty(UnknownType) {}
+
+ void run(Function *f) {
+ foreach (BasicBlock *bb, f->basicBlocks) {
+ _conversions.clear();
+
+ foreach (Stmt *s, bb->statements) {
+ _currStmt = s;
+ s->accept(this);
+ }
+
+ foreach (const Conversion &conversion, _conversions) {
+ if (conversion.stmt->asMove() && conversion.stmt->asMove()->source->asTemp()) {
+ *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType);
+ } else {
+ Temp *target = bb->TEMP(bb->newTemp());
+ target->type = conversion.targetType;
+ Expr *convert = bb->CONVERT(*conversion.expr, conversion.targetType);
+ Move *convCall = f->New<Move>();
+ convCall->init(target, convert, OpInvalid);
+
+ Temp *source = bb->TEMP(target->index);
+ source->type = conversion.targetType;
+ *conversion.expr = source;
+
+ int idx = bb->statements.indexOf(conversion.stmt);
+ bb->statements.insert(idx, convCall);
+ }
+ }
+ }
+ }
+
+protected:
+ virtual void visitConst(Const *c) {
+ if (_ty & NumberType && c->type & NumberType) {
+ c->type = _ty;
+ }
+ }
+
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitTemp(Temp *) {}
+ virtual void visitClosure(Closure *) {}
+ virtual void visitConvert(Convert *e) { run(e->expr, e->type); }
+ virtual void visitUnop(Unop *e) { run(e->expr, e->type); }
+ virtual 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:
+ case OpLShift:
+ case OpRShift:
+ case OpURShift:
+ run(e->left, e->type);
+ run(e->right, e->type);
+ break;
+
+ case OpGt:
+ case OpLt:
+ case OpGe:
+ case OpLe:
+ if (e->left->type == DoubleType)
+ run(e->right, DoubleType);
+ else if (e->right->type == DoubleType)
+ run(e->left, DoubleType);
+ else {
+ run(e->left, e->type);
+ run(e->right, e->type);
+ }
+ break;
+
+ case OpEqual:
+ case OpNotEqual:
+ case OpStrictEqual:
+ case OpStrictNotEqual:
+ break;
+
+ case OpInstanceof:
+ case OpIn:
+ run(e->left, e->type);
+ run(e->right, e->type);
+ break;
+
+ default:
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ }
+ }
+ virtual void visitCall(Call *e) {
+ run(e->base);
+ for (ExprList *it = e->args; it; it = it->next)
+ run(it->expr);
+ }
+ virtual void visitNew(New *e) {
+ run(e->base);
+ for (ExprList *it = e->args; it; it = it->next)
+ run(it->expr);
+ }
+ virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
+ virtual void visitMember(Member *e) { run(e->base); }
+ virtual void visitExp(Exp *s) { run(s->expr); }
+ virtual void visitMove(Move *s) {
+ run(s->target);
+ run(s->source, s->target->type);
+ }
+ virtual void visitJump(Jump *) {}
+ virtual void visitCJump(CJump *s) {
+ run(s->cond, BoolType);
+ }
+ virtual void visitRet(Ret *s) { run(s->expr); }
+ virtual void visitTry(Try *) {}
+ virtual void visitPhi(Phi *s) {
+ Type ty = s->targetTemp->type;
+ foreach (Expr *e, s->incoming)
+ if (e->asConst())
+ run(e, ty);
+ }
+};
+
+void doEdgeSplitting(Function *f)
+{
+ const QVector<BasicBlock *> oldBBs = f->basicBlocks;
+
+ foreach (BasicBlock *bb, oldBBs) {
+ if (bb->in.size() > 1) {
+ for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) {
+ BasicBlock *inBB = bb->in[inIdx];
+ if (inBB->out.size() > 1) { // this should have been split!
+#if defined(SHOW_SSA)
+ qDebug() << "Splitting edge from block" << inBB->index << "to block" << bb->index;
+#endif
+
+ // create the basic block:
+ BasicBlock *newBB = new BasicBlock(f, bb->containingGroup());
+ newBB->index = f->basicBlocks.last()->index + 1;
+ f->basicBlocks.append(newBB);
+ Jump *s = f->New<Jump>();
+ s->init(bb);
+ newBB->statements.append(s);
+
+ // rewire the old outgoing edge
+ int outIdx = inBB->out.indexOf(bb);
+ inBB->out[outIdx] = newBB;
+ newBB->in.append(inBB);
+
+ // rewire the old incoming edge
+ bb->in[inIdx] = newBB;
+ newBB->out.append(bb);
+
+ // patch the terminator
+ Stmt *terminator = inBB->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 {
+ Q_ASSERT(!"Unknown terminator!");
+ }
+ }
+ }
+ }
+ }
+}
+
+QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const DominatorTree &df)
+{
+ struct I {
+ const DominatorTree &df;
+ QHash<BasicBlock *, BasicBlock *> &startEndLoops;
+ QSet<BasicBlock *> visited;
+ QVector<BasicBlock *> &sequence;
+ BasicBlock *currentGroup;
+ QList<BasicBlock *> postponed;
+
+ I(const DominatorTree &df, QVector<BasicBlock *> &sequence,
+ QHash<BasicBlock *, BasicBlock *> &startEndLoops)
+ : df(df)
+ , sequence(sequence)
+ , startEndLoops(startEndLoops)
+ , currentGroup(0)
+ {}
+
+ void DFS(BasicBlock *bb) {
+ Q_ASSERT(bb);
+ if (visited.contains(bb))
+ return;
+
+ if (bb->containingGroup() != currentGroup) {
+ postponed.append(bb);
+ return;
+ }
+ if (bb->isGroupStart())
+ currentGroup = bb;
+ else if (bb->in.size() > 1)
+ foreach (BasicBlock *inBB, bb->in)
+ if (!visited.contains(inBB))
+ return;
+
+ Q_ASSERT(df.immediateDominator(bb) == 0 || sequence.contains(df.immediateDominator(bb)));
+ layout(bb);
+ if (Stmt *terminator = bb->terminator()) {
+ if (Jump *j = terminator->asJump()) {
+ Q_ASSERT(bb->out.size() == 1);
+ DFS(j->target);
+ } else if (CJump *cj = terminator->asCJump()) {
+ Q_ASSERT(bb->out.size() == 2);
+ DFS(cj->iftrue);
+ DFS(cj->iffalse);
+ } else if (terminator->asRet()) {
+ Q_ASSERT(bb->out.size() == 0);
+ // nothing to do.
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ if (bb->isGroupStart()) {
+ currentGroup = bb->containingGroup();
+ startEndLoops.insert(bb, sequence.last());
+ QList<BasicBlock *> p = postponed;
+ foreach (BasicBlock *pBB, p)
+ DFS(pBB);
+ }
+ }
+
+ void layout(BasicBlock *bb) {
+ sequence.append(bb);
+ visited.insert(bb);
+ postponed.removeAll(bb);
+ }
+ };
+
+ QVector<BasicBlock *> sequence;
+ sequence.reserve(function->basicBlocks.size());
+ QHash<BasicBlock *, BasicBlock *> startEndLoops;
+ I(df, sequence, startEndLoops).DFS(function->basicBlocks.first());
+ qSwap(function->basicBlocks, sequence);
+
+ showMeTheCode(function);
+ return startEndLoops;
+}
+
+void checkCriticalEdges(QVector<BasicBlock *> basicBlocks) {
+ foreach (BasicBlock *bb, basicBlocks) {
+ if (bb && bb->out.size() > 1) {
+ foreach (BasicBlock *bb2, bb->out) {
+ if (bb2 && bb2->in.size() > 1) {
+ qout << "found critical edge between block "
+ << bb->index << " and block " << bb2->index;
+ Q_ASSERT(false);
+ }
+ }
+ }
+ }
+}
+
+void cleanupBasicBlocks(Function *function)
+{
+// showMeTheCode(function);
+
+ // remove all basic blocks that have no incoming edges, but skip the entry block
+ QVector<BasicBlock *> W = function->basicBlocks;
+ W.removeFirst();
+ QSet<BasicBlock *> toRemove;
+
+ while (!W.isEmpty()) {
+ BasicBlock *bb = W.first();
+ W.removeFirst();
+ if (toRemove.contains(bb))
+ continue;
+ if (bb->in.isEmpty()) {
+ foreach (BasicBlock *outBB, bb->out) {
+ int idx = outBB->in.indexOf(bb);
+ if (idx != -1) {
+ outBB->in.remove(idx);
+ W.append(outBB);
+ }
+ }
+ toRemove.insert(bb);
+ }
+ }
+
+ // TODO: merge 2 basic blocks A and B if A has one outgoing edge (to B), B has one incoming
+ // edge (from A), but not when A has more than 1 incoming edge and B has more than one
+ // outgoing edge.
+
+ foreach (BasicBlock *bb, toRemove) {
+ foreach (Stmt *s, bb->statements)
+ s->destroyData();
+ int idx = function->basicBlocks.indexOf(bb);
+ if (idx != -1)
+ function->basicBlocks.remove(idx);
+ delete bb;
+ }
+
+ // re-number all basic blocks:
+ for (int i = 0; i < function->basicBlocks.size(); ++i)
+ function->basicBlocks[i]->index = i;
+}
+
+class InputOutputCollector: protected StmtVisitor, protected ExprVisitor {
+ const bool variablesCanEscape;
+
+public:
+ QList<Temp> inputs;
+ QList<Temp> outputs;
+
+ InputOutputCollector(bool variablesCanEscape): variablesCanEscape(variablesCanEscape) {}
+
+ void collect(Stmt *s) {
+ inputs.clear();
+ outputs.clear();
+ s->accept(this);
+ }
+
+protected:
+ virtual void visitConst(Const *) {}
+ virtual void visitString(String *) {}
+ virtual void visitRegExp(RegExp *) {}
+ virtual void visitName(Name *) {}
+ virtual void visitTemp(Temp *e) {
+ switch (e->kind) {
+ case Temp::Local:
+ if (!variablesCanEscape)
+ inputs.append(*e);
+ break;
+
+ case Temp::VirtualRegister:
+ inputs.append(*e);
+ break;
+
+ default:
+ break;
+ }
+ }
+ virtual void visitClosure(Closure *) {}
+ virtual void visitConvert(Convert *e) { e->expr->accept(this); }
+ virtual void visitUnop(Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitCall(Call *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+ virtual void visitNew(New *e) {
+ e->base->accept(this);
+ for (ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+ virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(Member *e) { e->base->accept(this); }
+ virtual void visitExp(Exp *s) { s->expr->accept(this); }
+ virtual void visitMove(Move *s) {
+ s->source->accept(this);
+ if (Temp *t = s->target->asTemp()) {
+ if ((t->kind == Temp::Local && !variablesCanEscape) || t->kind == Temp::VirtualRegister)
+ outputs.append(*t);
+ else
+ s->target->accept(this);
+ } else {
+ s->target->accept(this);
+ }
+ }
+ virtual void visitJump(Jump *) {}
+ virtual void visitCJump(CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(Ret *s) { s->expr->accept(this); }
+ virtual void visitTry(Try *) {}
+ virtual void visitPhi(Phi *s) {
+ // Handled separately
+ }
+};
+
+/*
+ * The algorithm is described in:
+ *
+ * Linear Scan Register Allocation on SSA Form
+ * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010
+ *
+ * There is one slight difference w.r.t. the phi-nodes: in the artice, the phi nodes are attached
+ * to the basic-blocks. Therefore, in the algorithm in the article, the ranges for input parameters
+ * for phi nodes run from their definition upto the branch instruction into the block with the phi
+ * node. In our representation, phi nodes are mostly treaded as normal instructions, so we have to
+ * enlarge the range to cover the phi node itself.
+ */
+class LifeRanges {
+ typedef QSet<Temp> LiveRegs;
+
+ QHash<BasicBlock *, LiveRegs> _liveIn;
+ QHash<Temp, LifeTimeInterval> _intervals;
+ QList<LifeTimeInterval> _sortedRanges;
+
+public:
+ LifeRanges(Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops)
+ {
+ int id = 0;
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ foreach (Stmt *s, bb->statements) {
+ if (s->asPhi())
+ s->id = id + 1;
+ else
+ s->id = ++id;
+ }
+ }
+
+ for (int i = function->basicBlocks.size() - 1; i >= 0; --i) {
+ BasicBlock *bb = function->basicBlocks[i];
+ buildIntervals(bb, startEndLoops.value(bb, 0), function->variablesCanEscape());
+ }
+
+ _sortedRanges.reserve(_intervals.size());
+ for (QHash<Temp, LifeTimeInterval>::const_iterator i = _intervals.begin(), ei = _intervals.end(); i != ei; ++i) {
+ LifeTimeInterval range = i.value();
+ range.setTemp(i.key());
+ _sortedRanges.append(range);
+ }
+ qSort(_sortedRanges.begin(), _sortedRanges.end(), LifeTimeInterval::lessThan);
+ }
+
+ QList<LifeTimeInterval> ranges() const { return _sortedRanges; }
+
+ void dump() const
+ {
+ qout << "Life ranges:" << endl;
+ qout << "Intervals:" << endl;
+ foreach (const LifeTimeInterval &range, _sortedRanges) {
+ range.dump();
+ qout << endl;
+ }
+
+ foreach (BasicBlock *bb, _liveIn.keys()) {
+ qout << "L" << bb->index <<" live-in: ";
+ QList<Temp> live = QList<Temp>::fromSet(_liveIn.value(bb));
+ qSort(live);
+ for (int i = 0; i < live.size(); ++i) {
+ if (i > 0) qout << ", ";
+ live[i].dump(qout);
+ }
+ qout << endl;
+ }
+ }
+
+private:
+ void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd, bool variablesCanEscape)
+ {
+ LiveRegs live;
+ foreach (BasicBlock *successor, bb->out) {
+ live.unite(_liveIn[successor]);
+ const int bbIndex = successor->in.indexOf(bb);
+ Q_ASSERT(bbIndex >= 0);
+
+ foreach (Stmt *s, successor->statements) {
+ if (Phi *phi = s->asPhi()) {
+ if (Temp *t = phi->incoming[bbIndex]->asTemp())
+ live.insert(*t);
+ } else {
+ break;
+ }
+ }
+ }
+
+ foreach (const Temp &opd, live)
+ _intervals[opd].addRange(bb->statements.first(), bb->statements.last());
+
+ InputOutputCollector collector(variablesCanEscape);
+ for (int i = bb->statements.size() - 1; i >= 0; --i) {
+ Stmt *s = bb->statements[i];
+ if (Phi *phi = s->asPhi()) {
+ live.remove(*phi->targetTemp);
+ continue;
+ }
+ collector.collect(s);
+ foreach (const Temp &opd, collector.outputs) {
+ _intervals[opd].setFrom(s);
+ live.remove(opd);
+ }
+ foreach (const Temp &opd, collector.inputs) {
+ _intervals[opd].addRange(bb->statements.first(), s);
+ live.insert(opd);
+ }
+ }
+
+ if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
+ foreach (const Temp &opd, live)
+ _intervals[opd].addRange(bb->statements.first(), loopEnd->statements.last());
+ }
+
+ _liveIn[bb] = live;
+ }
+};
+} // anonymous namespace
+
+void LifeTimeInterval::setFrom(Stmt *from) {
+ Q_ASSERT(from && from->id > 0);
+
+ if (_ranges.isEmpty()) // this is the case where there is no use, only a define
+ _ranges.push_front(Range(from->id, from->id));
+ else
+ _ranges.first().start = from->id;
+}
+
+void LifeTimeInterval::addRange(Stmt *from, Stmt *to) {
+ Q_ASSERT(from && from->id > 0);
+ Q_ASSERT(to && to->id > 0);
+ Q_ASSERT(to->id >= from->id);
+
+ if (_ranges.isEmpty()) {
+ _ranges.push_front(Range(from->id, to->id));
+ return;
+ }
+
+ Range *p = &_ranges.first();
+ if (to->id + 1 >= p->start && p->end + 1 >= from->id) {
+ p->start = qMin(p->start, from->id);
+ p->end = qMax(p->end, to->id);
+ while (_ranges.count() > 1) {
+ Range *p1 = &_ranges[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.pop_front();
+ p = &_ranges.first();
+ }
+ } else {
+ Q_ASSERT(to->id < p->start);
+ _ranges.push_front(Range(from->id, to->id));
+ }
+}
+
+void LifeTimeInterval::dump() const {
+ _temp.dump(qout);
+ qout << ": ";
+ if (_ranges.isEmpty())
+ qout << "(none)";
+ for (int i = 0; i < _ranges.size(); ++i) {
+ if (i > 0) qout << ", ";
+ qout << _ranges[i].start << " - " << _ranges[i].end;
+ }
+ if (_reg != Invalid)
+ qout << " (register " << _reg << ")";
+}
+
+bool LifeTimeInterval::lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2) {
+ if (r1._ranges.first().start == r2._ranges.first().start)
+ return r1._ranges.last().end < r2._ranges.last().end;
+ else
+ return r1._ranges.first().start < r2._ranges.first().start;
+}
+
+void Optimizer::run()
+{
+#if defined(SHOW_SSA)
+ qout << "##### NOW IN FUNCTION " << (_function->name ? qPrintable(*_function->name) : "anonymous!")
+ << " with " << _function->basicBlocks.size() << " basic blocks." << endl << flush;
+#endif
+
+ // Number all basic blocks, so we have nice numbers in the dumps:
+ for (int i = 0; i < function->basicBlocks.size(); ++i)
+ function->basicBlocks[i]->index = i;
+ showMeTheCode(function);
+
+ cleanupBasicBlocks(function);
+
+ function->removeSharedExpressions();
+
+// showMeTheCode(function);
+
+ if (!function->hasTry && !function->hasWith) {
+// qout << "Starting edge splitting..." << endl;
+ doEdgeSplitting(function);
+// showMeTheCode(function);
+
+ // Calculate the dominator tree:
+ DominatorTree df(function->basicBlocks);
+
+ convertToSSA(function, df);
+// showMeTheCode(function);
+
+// qout << "Starting def/uses calculation..." << endl;
+ DefUsesCalculator defUses(function);
+
+// qout << "Cleaning up phi nodes..." << endl;
+ cleanupPhis(defUses);
+// showMeTheCode(function);
+
+// qout << "Starting dead-code elimination..." << endl;
+ DeadCodeElimination(defUses, function).run();
+// showMeTheCode(function);
+
+// qout << "Running type inference..." << endl;
+ TypeInference(defUses).run(function);
+// showMeTheCode(function);
+
+// qout << "Doing type propagation..." << endl;
+ TypePropagation().run(function);
+// showMeTheCode(function);
+
+// qout << "Doing block scheduling..." << endl;
+ startEndLoops = scheduleBlocks(function, df);
+// showMeTheCode(function);
+
+#ifndef QT_NO_DEBUG
+ checkCriticalEdges(function->basicBlocks);
+#endif
+
+// qout << "Finished." << endl;
+ inSSA = true;
+ } else {
+ inSSA = false;
+ }
+}
+
+namespace {
+void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) {
+ if (target->type != source->type)
+ source = basicBlock->CONVERT(source, target->type);
+
+ Move *s = function->New<Move>();
+ s->init(target, source, OpInvalid);
+ basicBlock->statements.insert(basicBlock->statements.size() - 1, s);
+}
+}
+
+/*
+ * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This
+ * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the
+ * same time. That way the huge number of redundant moves generated by this function are eliminated.
+ */
+void Optimizer::convertOutOfSSA() {
+ // We assume that edge-splitting is already done.
+ foreach (BasicBlock *bb, function->basicBlocks) {
+ QVector<Stmt *> &stmts = bb->statements;
+ while (!stmts.isEmpty()) {
+ Stmt *s = stmts.first();
+ if (Phi *phi = s->asPhi()) {
+ stmts.removeFirst();
+ for (int i = 0, ei = phi->incoming.size(); i != ei; ++i)
+ insertMove(function, bb->in[i], phi->targetTemp, phi->incoming[i]);
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+QList<Optimizer::SSADeconstructionMove> Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock)
+{
+ QList<SSADeconstructionMove> moves;
+
+ foreach (BasicBlock *outEdge, basicBlock->out) {
+ int inIdx = outEdge->in.indexOf(basicBlock);
+ Q_ASSERT(inIdx >= 0);
+ foreach (Stmt *s, outEdge->statements) {
+ if (Phi *phi = s->asPhi()) {
+ SSADeconstructionMove m;
+ m.source = phi->incoming[inIdx];
+ m.target = phi->targetTemp;
+ moves.append(m);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return moves;
+}
+
+QList<LifeTimeInterval> Optimizer::lifeRanges() const
+{
+ Q_ASSERT(isInSSA());
+
+ LifeRanges lifeRanges(function, startEndLoops);
+// lifeRanges.dump();
+// showMeTheCode(function);
+ return lifeRanges.ranges();
+}
diff --git a/src/qml/qml/v4/qv4ssa_p.h b/src/qml/qml/v4/qv4ssa_p.h
new file mode 100644
index 0000000000..097a40eff1
--- /dev/null
+++ b/src/qml/qml/v4/qv4ssa_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SSA_P_H
+#define QV4SSA_P_H
+
+#include "qv4jsir_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace V4IR {
+
+class LifeTimeInterval {
+ struct Range {
+ int start;
+ int end;
+
+ Range(int start = Invalid, int end = Invalid)
+ : start(start)
+ , end(end)
+ {}
+ };
+
+ Temp _temp;
+ QList<Range> _ranges;
+ int _reg;
+
+public:
+ static const int Invalid = -1;
+
+ LifeTimeInterval()
+ : _reg(Invalid)
+ {}
+
+ void setTemp(const Temp &temp) { this->_temp = temp; }
+ Temp temp() const { return _temp; }
+
+ void setFrom(Stmt *from);
+ void addRange(Stmt *from, Stmt *to);
+
+ int start() const { return _ranges.first().start; }
+ int end() const { return _ranges.last().end; }
+
+ int reg() const { return _reg; }
+ void setReg(int reg) { _reg = reg; }
+
+ void dump() const;
+ static bool lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2);
+};
+
+class Optimizer
+{
+public:
+ struct SSADeconstructionMove
+ {
+ Expr *source;
+ Temp *target;
+
+ bool needsConversion() const
+ { return target->type != source->type; }
+ };
+
+public:
+ Optimizer(Function *function)
+ : function(function)
+ , inSSA(false)
+ {}
+
+ void run();
+ void convertOutOfSSA();
+
+ bool isInSSA() const
+ { return inSSA; }
+
+ QList<SSADeconstructionMove> ssaDeconstructionMoves(BasicBlock *basicBlock);
+
+ QList<LifeTimeInterval> lifeRanges() const;
+
+private:
+ Function *function;
+ bool inSSA;
+ QHash<BasicBlock *, BasicBlock *> startEndLoops;
+};
+
+} // V4IR namespace
+} // QQmlJS namespace
+QT_END_NAMESPACE
+
+#endif // QV4SSA_P_H
diff --git a/src/qml/qml/v4/qv4stacktrace.cpp b/src/qml/qml/v4/qv4stacktrace.cpp
new file mode 100644
index 0000000000..1cc2e53556
--- /dev/null
+++ b/src/qml/qml/v4/qv4stacktrace.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <DbgHelp.h>
+#endif
+
+#include "qv4stacktrace_p.h"
+#include "qv4function_p.h"
+#include "qv4engine_p.h"
+#include "qv4unwindhelper_p.h"
+
+#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC)
+#define HAVE_GNU_BACKTRACE
+#include <execinfo.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+NativeStackTrace::NativeStackTrace(ExecutionContext *context)
+{
+ engine = context->engine;
+ currentNativeFrame = 0;
+
+#if defined(HAVE_GNU_BACKTRACE)
+ UnwindHelper::prepareForUnwind(context);
+
+ nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0]));
+#elif defined(Q_OS_WIN)
+
+ int machineType = 0;
+
+ CONTEXT winContext;
+ memset(&winContext, 0, sizeof(winContext));
+ winContext.ContextFlags = CONTEXT_FULL;
+ RtlCaptureContext(&winContext);
+
+ STACKFRAME64 sf64;
+ memset(&sf64, 0, sizeof(sf64));
+
+#if defined(Q_PROCESSOR_X86_32)
+ machineType = IMAGE_FILE_MACHINE_I386;
+
+ sf64.AddrFrame.Offset = winContext.Ebp;
+ sf64.AddrFrame.Mode = AddrModeFlat;
+ sf64.AddrPC.Offset = winContext.Eip;
+ sf64.AddrPC.Mode = AddrModeFlat;
+ sf64.AddrStack.Offset = winContext.Esp;
+ sf64.AddrStack.Mode = AddrModeFlat;
+
+#elif defined(Q_PROCESSOR_X86_64)
+ machineType = IMAGE_FILE_MACHINE_AMD64;
+
+ sf64.AddrFrame.Offset = winContext.Rbp;
+ sf64.AddrFrame.Mode = AddrModeFlat;
+ sf64.AddrPC.Offset = winContext.Rip;
+ sf64.AddrPC.Mode = AddrModeFlat;
+ sf64.AddrStack.Offset = winContext.Rsp;
+ sf64.AddrStack.Mode = AddrModeFlat;
+
+#else
+#error "Platform unsupported!"
+#endif
+
+ nativeFrameCount = 0;
+
+ while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) {
+
+ if (sf64.AddrReturn.Offset == 0)
+ break;
+
+ trace[nativeFrameCount] = reinterpret_cast<void*>(sf64.AddrReturn.Offset);
+ nativeFrameCount++;
+ if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0]))
+ break;
+ }
+
+#else
+ nativeFrameCount = 0;
+#endif
+ }
+
+NativeFrame NativeStackTrace::nextFrame() {
+ NativeFrame frame;
+ frame.function = 0;
+ frame.line = -1;
+
+ for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) {
+ quintptr pc = reinterpret_cast<quintptr>(trace[currentNativeFrame]);
+ // The pointers from the back trace point to the return address, but we are interested in
+ // the caller site.
+ pc = pc - 1;
+
+ Function *f = engine->functionForProgramCounter(pc);
+ if (!f)
+ continue;
+
+ frame.function = f;
+ frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast<quintptr>(f->code));
+ }
+
+ return frame;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4stacktrace_p.h b/src/qml/qml/v4/qv4stacktrace_p.h
new file mode 100644
index 0000000000..79cb4d1813
--- /dev/null
+++ b/src/qml/qml/v4/qv4stacktrace_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STACKTRACE_P_H
+#define QV4STACKTRACE_P_H
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+struct ExecutionEngine;
+struct ExecutionContext;
+
+struct NativeFrame {
+ Function *function;
+ int line;
+};
+
+struct NativeStackTrace
+{
+ void *trace[100];
+ int nativeFrameCount;
+ int currentNativeFrame;
+ ExecutionEngine *engine;
+
+ NativeStackTrace(ExecutionContext *context);
+
+ NativeFrame nextFrame();
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4STACKTRACE_P_H
diff --git a/src/qml/qml/v4/qv4string.cpp b/src/qml/qml/v4/qv4string.cpp
new file mode 100644
index 0000000000..bfdee2d2cc
--- /dev/null
+++ b/src/qml/qml/v4/qv4string.cpp
@@ -0,0 +1,304 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4string_p.h"
+#include "qv4identifiertable_p.h"
+#include "qv4runtime_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4stringobject_p.h"
+#include <QtCore/QHash>
+
+using namespace QV4;
+
+static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok)
+{
+ *ok = false;
+ uint i = ch->unicode() - '0';
+ if (i > 9)
+ return UINT_MAX;
+ ++ch;
+ // reject "01", "001", ...
+ if (i == 0 && ch != end)
+ return UINT_MAX;
+
+ while (ch < end) {
+ uint x = ch->unicode() - '0';
+ if (x > 9)
+ return UINT_MAX;
+ uint n = i*10 + x;
+ if (n < i)
+ // overflow
+ return UINT_MAX;
+ i = n;
+ ++ch;
+ }
+ *ok = true;
+ return i;
+}
+
+static uint toArrayIndex(const char *ch, const char *end, bool *ok)
+{
+ *ok = false;
+ uint i = *ch - '0';
+ if (i > 9)
+ return UINT_MAX;
+ ++ch;
+ // reject "01", "001", ...
+ if (i == 0 && ch != end)
+ return UINT_MAX;
+
+ while (ch < end) {
+ uint x = *ch - '0';
+ if (x > 9)
+ return UINT_MAX;
+ uint n = i*10 + x;
+ if (n < i)
+ // overflow
+ return UINT_MAX;
+ i = n;
+ ++ch;
+ }
+ *ok = true;
+ return i;
+}
+
+
+const ManagedVTable String::static_vtbl =
+{
+ call,
+ construct,
+ 0 /*markObjects*/,
+ destroy,
+ 0 /*collectDeletables*/,
+ hasInstance,
+ get,
+ getIndexed,
+ put,
+ putIndexed,
+ query,
+ queryIndexed,
+ deleteProperty,
+ deleteIndexedProperty,
+ 0 /*getLookup*/,
+ 0 /*setLookup*/,
+ Managed::isEqualTo,
+ 0 /*advanceIterator*/,
+ "String",
+};
+
+void String::destroy(Managed *that)
+{
+ static_cast<String*>(that)->~String();
+}
+
+Value String::get(Managed *m, String *name, bool *hasProperty)
+{
+ String *that = static_cast<String *>(m);
+ ExecutionEngine *v4 = m->engine();
+ if (name == v4->id_length) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromInt32(that->_text.length());
+ }
+ PropertyAttributes attrs;
+ Property *pd = v4->stringPrototype->__getPropertyDescriptor__(name, &attrs);
+ if (!pd || attrs.isGeneric()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+ }
+ if (hasProperty)
+ *hasProperty = true;
+ return v4->stringPrototype->getValue(Value::fromString(that), pd, attrs);
+}
+
+Value String::getIndexed(Managed *m, uint index, bool *hasProperty)
+{
+ String *that = static_cast<String *>(m);
+ ExecutionEngine *engine = that->engine();
+ if (index < that->_text.length()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromString(engine->newString(that->toQString().mid(index, 1)));
+ }
+ PropertyAttributes attrs;
+ Property *pd = engine->stringPrototype->__getPropertyDescriptor__(index, &attrs);
+ if (!pd || attrs.isGeneric()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+ }
+ if (hasProperty)
+ *hasProperty = true;
+ return engine->stringPrototype->getValue(Value::fromString(that), pd, attrs);
+}
+
+void String::put(Managed *m, String *name, const Value &value)
+{
+ String *that = static_cast<String *>(m);
+ Object *o = that->engine()->newStringObject(Value::fromString(that));
+ o->put(name, value);
+}
+
+void String::putIndexed(Managed *m, uint index, const Value &value)
+{
+ String *that = static_cast<String *>(m);
+ Object *o = m->engine()->newStringObject(Value::fromString(that));
+ o->putIndexed(index, value);
+}
+
+PropertyAttributes String::query(const Managed *m, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return queryIndexed(m, idx);
+ return Attr_Invalid;
+}
+
+PropertyAttributes String::queryIndexed(const Managed *m, uint index)
+{
+ const String *that = static_cast<const String *>(m);
+ return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid;
+}
+
+bool String::deleteProperty(Managed *, String *)
+{
+ return false;
+}
+
+bool String::deleteIndexedProperty(Managed *m, uint index)
+{
+ return false;
+}
+
+String::String(ExecutionEngine *engine, const QString &text)
+ : Managed(engine ? engine->emptyClass : 0), _text(text), identifier(0), stringHash(UINT_MAX)
+{
+ vtbl = &static_vtbl;
+ type = Type_String;
+ subtype = StringType_Unknown;
+}
+
+uint String::toUInt(bool *ok) const
+{
+ *ok = true;
+
+ if (subtype == StringType_Unknown)
+ createHashValue();
+ if (subtype >= StringType_UInt)
+ return stringHash;
+
+ // ### this conversion shouldn't be required
+ double d = __qmljs_string_to_number(toQString());
+ uint l = (uint)d;
+ if (d == l)
+ return l;
+ *ok = false;
+ return UINT_MAX;
+}
+
+void String::makeIdentifierImpl()
+{
+ engine()->identifierTable->identifier(this);
+}
+
+void String::createHashValue() const
+{
+ const QChar *ch = _text.constData();
+ const QChar *end = ch + _text.length();
+
+ // array indices get their number as hash value
+ bool ok;
+ stringHash = toArrayIndex(ch, end, &ok);
+ if (ok) {
+ subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex;
+ return;
+ }
+
+ uint h = 0xffffffff;
+ while (ch < end) {
+ h = 31 * h + ch->unicode();
+ ++ch;
+ }
+
+ stringHash = h;
+ subtype = StringType_Regular;
+}
+
+uint String::createHashValue(const QChar *ch, int length)
+{
+ const QChar *end = ch + length;
+
+ // array indices get their number as hash value
+ bool ok;
+ uint stringHash = toArrayIndex(ch, end, &ok);
+ if (ok)
+ return stringHash;
+
+ uint h = 0xffffffff;
+ while (ch < end) {
+ h = 31 * h + ch->unicode();
+ ++ch;
+ }
+
+ return h;
+}
+
+uint String::createHashValue(const char *ch, int length)
+{
+ const char *end = ch + length;
+
+ // array indices get their number as hash value
+ bool ok;
+ uint stringHash = toArrayIndex(ch, end, &ok);
+ if (ok)
+ return stringHash;
+
+ uint h = 0xffffffff;
+ while (ch < end) {
+ if (*ch >= 0x80)
+ return UINT_MAX;
+ h = 31 * h + *ch;
+ ++ch;
+ }
+
+ return h;
+}
diff --git a/src/qml/qml/v4/qv4string_p.h b/src/qml/qml/v4/qv4string_p.h
new file mode 100644
index 0000000000..629fa506b7
--- /dev/null
+++ b/src/qml/qml/v4/qv4string_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STRING_H
+#define QV4STRING_H
+
+#include <QtCore/qstring.h>
+#include "qv4managed_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct ExecutionEngine;
+struct Identifier;
+
+struct Q_QML_EXPORT String : public Managed {
+ enum StringType {
+ StringType_Unknown,
+ StringType_Regular,
+ StringType_UInt,
+ StringType_ArrayIndex
+ };
+
+ String() : Managed(0), identifier(0), stringHash(UINT_MAX)
+ { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; }
+ String(ExecutionEngine *engine, const QString &text);
+ ~String() { _data = 0; }
+
+ inline bool isEqualTo(const String *other) const {
+ if (this == other)
+ return true;
+ if (hashValue() != other->hashValue())
+ return false;
+ if (identifier && identifier == other->identifier)
+ return true;
+ if (subtype >= StringType_UInt && subtype == other->subtype)
+ return true;
+
+ return toQString() == other->toQString();
+ }
+ inline bool compare(const String *other) {
+ return toQString() < other->toQString();
+ }
+
+ inline bool isEmpty() const { return _text.isEmpty(); }
+ inline const QString &toQString() const {
+ return _text;
+ }
+
+ inline unsigned hashValue() const {
+ if (subtype == StringType_Unknown)
+ createHashValue();
+
+ return stringHash;
+ }
+ uint asArrayIndex() const {
+ if (subtype == StringType_Unknown)
+ createHashValue();
+ if (subtype == StringType_ArrayIndex)
+ return stringHash;
+ return UINT_MAX;
+ }
+ uint toUInt(bool *ok) const;
+
+ void makeIdentifier() {
+ if (identifier)
+ return;
+ makeIdentifierImpl();
+ }
+
+ void makeIdentifierImpl();
+
+ void createHashValue() const;
+ static uint createHashValue(const QChar *ch, int length);
+ static uint createHashValue(const char *ch, int length);
+
+ bool startsWithUpper() const {
+ return _text.length() && _text.at(0).isUpper();
+ }
+ int length() const {
+ return _text.length();
+ }
+
+ QString _text;
+ mutable Identifier *identifier;
+ mutable uint stringHash;
+
+protected:
+ static void destroy(Managed *);
+ static Value get(Managed *m, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, uint index, bool *hasProperty);
+ static void put(Managed *m, String *name, const Value &value);
+ static void putIndexed(Managed *m, uint index, const Value &value);
+ static PropertyAttributes query(const Managed *m, String *name);
+ static PropertyAttributes queryIndexed(const Managed *m, uint index);
+ static bool deleteProperty(Managed *, String *);
+ static bool deleteIndexedProperty(Managed *m, uint index);
+
+ static const ManagedVTable static_vtbl;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4stringobject.cpp b/src/qml/qml/v4/qv4stringobject.cpp
new file mode 100644
index 0000000000..5afedd3d4f
--- /dev/null
+++ b/src/qml/qml/v4/qv4stringobject.cpp
@@ -0,0 +1,761 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4stringobject_p.h"
+#include "qv4regexpobject_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4mm_p.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDateTime>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+#include <cmath>
+#include <qmath.h>
+#include <qnumeric.h>
+#include <cassert>
+
+#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 <qv4isel_masm_p.h>
+
+#ifndef Q_OS_WIN
+# include <time.h>
+# ifndef Q_OS_VXWORKS
+# include <sys/time.h>
+# else
+# include "qplatformdefs.h"
+# endif
+#else
+# include <windows.h>
+#endif
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(StringObject);
+
+StringObject::StringObject(ExecutionEngine *engine, const Value &value)
+ : Object(engine), value(value)
+{
+ vtbl = &static_vtbl;
+ type = Type_StringObject;
+
+ tmpProperty.value = Value::undefinedValue();
+
+ assert(value.isString());
+ defineReadonlyProperty(engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length()));
+}
+
+Property *StringObject::getIndex(uint index) const
+{
+ QString str = value.stringValue()->toQString();
+ if (index >= (uint)str.length())
+ return 0;
+ String *result = internalClass->engine->newString(str.mid(index, 1));
+ tmpProperty.value = Value::fromString(result);
+ return &tmpProperty;
+}
+
+bool StringObject::deleteIndexedProperty(Managed *m, uint index)
+{
+ StringObject *o = m->asStringObject();
+ if (!o)
+ m->engine()->current->throwTypeError();
+
+ if (index < o->value.stringValue()->toQString().length()) {
+ if (m->engine()->current->strictMode)
+ m->engine()->current->throwTypeError();
+ return false;
+ }
+ return true;
+}
+
+Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs)
+{
+ StringObject *s = static_cast<StringObject *>(m);
+ uint slen = s->value.stringValue()->toQString().length();
+ if (it->arrayIndex < slen) {
+ while (it->arrayIndex < slen) {
+ *index = it->arrayIndex;
+ ++it->arrayIndex;
+ if (attrs)
+ *attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable);
+ return s->__getOwnProperty__(*index);
+ }
+ it->arrayNode = s->sparseArrayBegin();
+ // iterate until we're past the end of the string
+ while (it->arrayNode && it->arrayNode->key() < slen)
+ it->arrayNode = it->arrayNode->nextNode();
+ }
+
+ return Object::advanceIterator(m, it, name, index, attrs);
+}
+
+void StringObject::markObjects(Managed *that)
+{
+ StringObject *o = static_cast<StringObject *>(that);
+ o->value.stringValue()->mark();
+ Object::markObjects(that);
+}
+
+DEFINE_MANAGED_VTABLE(StringCtor);
+
+StringCtor::StringCtor(ExecutionContext *scope)
+ : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("String")))
+{
+ vtbl = &static_vtbl;
+}
+
+Value StringCtor::construct(Managed *m, Value *argv, int argc)
+{
+ Value value;
+ if (argc)
+ value = Value::fromString(argv[0].toString(m->engine()->current));
+ else
+ value = Value::fromString(m->engine()->current, QString());
+ return Value::fromObject(m->engine()->newStringObject(value));
+}
+
+Value StringCtor::call(Managed *m, const Value &, Value *argv, int argc)
+{
+ Value value;
+ if (argc)
+ value = Value::fromString(argv[0].toString(m->engine()->current));
+ else
+ value = Value::fromString(m->engine()->current, QString());
+ return value;
+}
+
+void StringPrototype::init(ExecutionEngine *engine, const Value &ctor)
+{
+ ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(this));
+ ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1));
+ ctor.objectValue()->defineDefaultProperty(engine, QStringLiteral("fromCharCode"), method_fromCharCode, 1);
+
+ defineDefaultProperty(engine, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(engine, QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(engine, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical
+ defineDefaultProperty(engine, QStringLiteral("charAt"), method_charAt, 1);
+ defineDefaultProperty(engine, QStringLiteral("charCodeAt"), method_charCodeAt, 1);
+ defineDefaultProperty(engine, QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(engine, QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(engine, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
+ defineDefaultProperty(engine, QStringLiteral("localeCompare"), method_localeCompare, 1);
+ defineDefaultProperty(engine, QStringLiteral("match"), method_match, 1);
+ defineDefaultProperty(engine, QStringLiteral("replace"), method_replace, 2);
+ defineDefaultProperty(engine, QStringLiteral("search"), method_search, 1);
+ defineDefaultProperty(engine, QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(engine, QStringLiteral("split"), method_split, 2);
+ defineDefaultProperty(engine, QStringLiteral("substr"), method_substr, 2);
+ defineDefaultProperty(engine, QStringLiteral("substring"), method_substring, 2);
+ defineDefaultProperty(engine, QStringLiteral("toLowerCase"), method_toLowerCase);
+ defineDefaultProperty(engine, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
+ defineDefaultProperty(engine, QStringLiteral("toUpperCase"), method_toUpperCase);
+ defineDefaultProperty(engine, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
+ defineDefaultProperty(engine, QStringLiteral("trim"), method_trim);
+}
+
+static QString getThisString(ExecutionContext *ctx)
+{
+ String* str = 0;
+ Value thisObject = ctx->thisObject;
+ if (StringObject *thisString = thisObject.asStringObject())
+ str = thisString->value.stringValue();
+ else if (thisObject.isUndefined() || thisObject.isNull())
+ ctx->throwTypeError();
+ else
+ str = ctx->thisObject.toString(ctx);
+ return str->toQString();
+}
+
+static QString getThisString(ExecutionContext *context, Value thisObject)
+{
+ if (thisObject.isString())
+ return thisObject.stringValue()->toQString();
+
+ String* str = 0;
+ if (StringObject *thisString = thisObject.asStringObject())
+ str = thisString->value.stringValue();
+ else if (thisObject.isUndefined() || thisObject.isNull())
+ context->throwTypeError();
+ else
+ str = thisObject.toString(context);
+ return str->toQString();
+}
+
+Value StringPrototype::method_toString(SimpleCallContext *context)
+{
+ if (context->thisObject.isString())
+ return context->thisObject;
+
+ StringObject *o = context->thisObject.asStringObject();
+ if (!o)
+ context->throwTypeError();
+ return o->value;
+}
+
+Value StringPrototype::method_charAt(SimpleCallContext *context)
+{
+ const QString str = getThisString(context, context->thisObject);
+
+ int pos = 0;
+ if (context->argumentCount > 0)
+ pos = (int) context->arguments[0].toInteger();
+
+ QString result;
+ if (pos >= 0 && pos < str.length())
+ result += str.at(pos);
+
+ return Value::fromString(context, result);
+}
+
+Value StringPrototype::method_charCodeAt(SimpleCallContext *context)
+{
+ const QString str = getThisString(context, context->thisObject);
+
+ int pos = 0;
+ if (context->argumentCount > 0)
+ pos = (int) context->arguments[0].toInteger();
+
+
+ if (pos >= 0 && pos < str.length())
+ return Value::fromInt32(str.at(pos).unicode());
+
+ return Value::fromDouble(qSNaN());
+}
+
+Value StringPrototype::method_concat(SimpleCallContext *context)
+{
+ QString value = getThisString(context, context->thisObject);
+
+ for (int i = 0; i < context->argumentCount; ++i) {
+ Value v = __qmljs_to_string(context->arguments[i], context);
+ assert(v.isString());
+ value += v.stringValue()->toQString();
+ }
+
+ return Value::fromString(context, value);
+}
+
+Value StringPrototype::method_indexOf(SimpleCallContext *context)
+{
+ QString value = getThisString(context, context->thisObject);
+
+ QString searchString;
+ if (context->argumentCount)
+ searchString = context->arguments[0].toString(context)->toQString();
+
+ int pos = 0;
+ if (context->argumentCount > 1)
+ pos = (int) context->arguments[1].toInteger();
+
+ int index = -1;
+ if (! value.isEmpty())
+ index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
+
+ return Value::fromDouble(index);
+}
+
+Value StringPrototype::method_lastIndexOf(SimpleCallContext *context)
+{
+ const QString value = getThisString(context, context->thisObject);
+
+ QString searchString;
+ if (context->argumentCount) {
+ Value v = __qmljs_to_string(context->arguments[0], context);
+ searchString = v.stringValue()->toQString();
+ }
+
+ Value posArg = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue();
+ double position = __qmljs_to_number(posArg);
+ if (std::isnan(position))
+ position = +qInf();
+ else
+ position = trunc(position);
+
+ int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
+ if (!searchString.isEmpty() && pos == value.length())
+ --pos;
+ if (searchString.isNull() && pos == 0)
+ return Value::fromDouble(-1);
+ int index = value.lastIndexOf(searchString, pos);
+ return Value::fromDouble(index);
+}
+
+Value StringPrototype::method_localeCompare(SimpleCallContext *context)
+{
+ const QString value = getThisString(context, context->thisObject);
+ const QString that = (context->argumentCount ? context->arguments[0] : Value::undefinedValue()).toString(context)->toQString();
+ return Value::fromDouble(QString::localeAwareCompare(value, that));
+}
+
+Value StringPrototype::method_match(SimpleCallContext *context)
+{
+ if (context->thisObject.isUndefined() || context->thisObject.isNull())
+ context->throwTypeError();
+
+ String *s = context->thisObject.toString(context);
+
+ Value regexp = context->argumentCount ? context->arguments[0] : Value::undefinedValue();
+ RegExpObject *rx = regexp.as<RegExpObject>();
+ if (!rx)
+ rx = context->engine->regExpCtor.asFunctionObject()->construct(&regexp, 1).as<RegExpObject>();
+
+ if (!rx)
+ // ### CHECK
+ context->throwTypeError();
+
+ bool global = rx->global;
+
+ // ### use the standard builtin function, not the one that might be redefined in the proto
+ FunctionObject *exec = context->engine->regExpPrototype->get(context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject();
+
+ Value arg = Value::fromString(s);
+ if (!global)
+ return exec->call(Value::fromObject(rx), &arg, 1);
+
+ String *lastIndex = context->engine->newString(QStringLiteral("lastIndex"));
+ rx->put(lastIndex, Value::fromInt32(0));
+ ArrayObject *a = context->engine->newArrayObject();
+
+ double previousLastIndex = 0;
+ uint n = 0;
+ while (1) {
+ Value result = exec->call(Value::fromObject(rx), &arg, 1);
+ if (result.isNull())
+ break;
+ assert(result.isObject());
+ double thisIndex = rx->get(lastIndex, 0).toInteger();
+ if (previousLastIndex == thisIndex) {
+ previousLastIndex = thisIndex + 1;
+ rx->put(lastIndex, Value::fromDouble(previousLastIndex));
+ } else {
+ previousLastIndex = thisIndex;
+ }
+ Value matchStr = result.objectValue()->getIndexed(0);
+ a->arraySet(n, matchStr);
+ ++n;
+ }
+ if (!n)
+ return Value::nullValue();
+
+ return Value::fromObject(a);
+
+}
+
+static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount)
+{
+ QString result;
+ result.reserve(replaceValue.length());
+ for (int i = 0; i < replaceValue.length(); ++i) {
+ if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) {
+ char ch = replaceValue.at(++i).toLatin1();
+ uint substStart = JSC::Yarr::offsetNoMatch;
+ uint substEnd = JSC::Yarr::offsetNoMatch;
+ if (ch == '$') {
+ result += ch;
+ continue;
+ } else if (ch == '&') {
+ substStart = matchOffsets[0];
+ substEnd = matchOffsets[1];
+ } else if (ch == '`') {
+ substStart = 0;
+ substEnd = matchOffsets[0];
+ } else if (ch == '\'') {
+ substStart = matchOffsets[1];
+ substEnd = input.length();
+ } else if (ch >= '1' && ch <= '9') {
+ char capture = ch - '0';
+ if (capture > 0 && capture < captureCount) {
+ substStart = matchOffsets[capture * 2];
+ substEnd = matchOffsets[capture * 2 + 1];
+ }
+ } else if (ch == '0' && i < replaceValue.length() - 1) {
+ int capture = (ch - '0') * 10;
+ ch = replaceValue.at(++i).toLatin1();
+ capture += ch - '0';
+ if (capture > 0 && capture < captureCount) {
+ substStart = matchOffsets[capture * 2];
+ substEnd = matchOffsets[capture * 2 + 1];
+ }
+ }
+ if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
+ result += input.midRef(substStart, substEnd - substStart);
+ } else {
+ result += replaceValue.at(i);
+ }
+ }
+ return result;
+}
+
+Value StringPrototype::method_replace(SimpleCallContext *ctx)
+{
+ QString string;
+ if (StringObject *thisString = ctx->thisObject.asStringObject())
+ string = thisString->value.stringValue()->toQString();
+ else
+ string = ctx->thisObject.toString(ctx)->toQString();
+
+ int numCaptures = 0;
+ QVarLengthArray<uint, 16> matchOffsets;
+ int numStringMatches = 0;
+
+ Value searchValue = ctx->argument(0);
+ RegExpObject *regExp = searchValue.as<RegExpObject>();
+ if (regExp) {
+ uint offset = 0;
+ while (true) {
+ int oldSize = matchOffsets.size();
+ matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2);
+ if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) {
+ matchOffsets.resize(oldSize);
+ break;
+ }
+ if (!regExp->global)
+ break;
+ offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
+ }
+ if (regExp->global)
+ regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0);
+ numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2);
+ numCaptures = regExp->value->captureCount();
+ } else {
+ numCaptures = 1;
+ QString searchString = searchValue.toString(ctx)->toQString();
+ int idx = string.indexOf(searchString);
+ if (idx != -1) {
+ numStringMatches = 1;
+ matchOffsets.resize(2);
+ matchOffsets[0] = idx;
+ matchOffsets[1] = idx + searchString.length();
+ }
+ }
+
+ QString result = string;
+ Value replaceValue = ctx->argument(1);
+ if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) {
+ int replacementDelta = 0;
+ int argc = numCaptures + 2;
+ Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value));
+ for (int i = 0; i < numStringMatches; ++i) {
+ for (int k = 0; k < numCaptures; ++k) {
+ int idx = (i * numCaptures + k) * 2;
+ uint start = matchOffsets[idx];
+ uint end = matchOffsets[idx + 1];
+ Value entry = Value::undefinedValue();
+ if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
+ entry = Value::fromString(ctx, string.mid(start, end - start));
+ args[k] = entry;
+ }
+ uint matchStart = matchOffsets[i * numCaptures * 2];
+ uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
+ args[numCaptures] = Value::fromUInt32(matchStart);
+ args[numCaptures + 1] = Value::fromString(ctx, string);
+ Value replacement = searchCallback->call(Value::undefinedValue(), args, argc);
+ QString replacementString = replacement.toString(ctx)->toQString();
+ result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString);
+ replacementDelta += replacementString.length() - matchEnd + matchStart;
+ }
+ } else {
+ QString newString = replaceValue.toString(ctx)->toQString();
+ int replacementDelta = 0;
+
+ for (int i = 0; i < numStringMatches; ++i) {
+ int baseIndex = i * numCaptures * 2;
+ uint matchStart = matchOffsets[baseIndex];
+ uint matchEnd = matchOffsets[baseIndex + 1];
+ if (matchStart == JSC::Yarr::offsetNoMatch)
+ continue;
+
+ QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures);
+ result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement);
+ replacementDelta += replacement.length() - matchEnd + matchStart;
+ }
+ }
+
+ return Value::fromString(ctx, result);
+}
+
+Value StringPrototype::method_search(SimpleCallContext *ctx)
+{
+ QString string;
+ if (StringObject *thisString = ctx->thisObject.asStringObject())
+ string = thisString->value.stringValue()->toQString();
+ else
+ string = ctx->thisObject.toString(ctx)->toQString();
+
+ Value regExpValue = ctx->argument(0);
+ RegExpObject *regExp = regExpValue.as<RegExpObject>();
+ if (!regExp) {
+ regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(&regExpValue, 1);
+ regExp = regExpValue.as<RegExpObject>();
+ }
+ uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint));
+ uint result = regExp->value->match(string, /*offset*/0, matchOffsets);
+ if (result == JSC::Yarr::offsetNoMatch)
+ return Value::fromInt32(-1);
+ return Value::fromUInt32(result);
+}
+
+Value StringPrototype::method_slice(SimpleCallContext *ctx)
+{
+ const QString text = getThisString(ctx);
+ const double length = text.length();
+
+ double start = ctx->argument(0).toInteger();
+ double end = ctx->argument(1).isUndefined()
+ ? length : ctx->argument(1).toInteger();
+
+ if (start < 0)
+ start = qMax(length + start, 0.);
+ else
+ start = qMin(start, length);
+
+ if (end < 0)
+ end = qMax(length + end, 0.);
+ else
+ end = qMin(end, length);
+
+ const int intStart = int(start);
+ const int intEnd = int(end);
+
+ int count = qMax(0, intEnd - intStart);
+ return Value::fromString(ctx, text.mid(intStart, count));
+}
+
+Value StringPrototype::method_split(SimpleCallContext *ctx)
+{
+ QString text;
+ if (StringObject *thisObject = ctx->thisObject.asStringObject())
+ text = thisObject->value.stringValue()->toQString();
+ else
+ text = ctx->thisObject.toString(ctx)->toQString();
+
+ Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue();
+ Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue();
+
+ ArrayObject* array = ctx->engine->newArrayObject();
+ Value result = Value::fromObject(array);
+
+ if (separatorValue.isUndefined()) {
+ if (limitValue.isUndefined()) {
+ array->push_back(Value::fromString(ctx, text));
+ return result;
+ }
+ return Value::fromString(ctx, text.left(limitValue.toInteger()));
+ }
+
+ uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32();
+
+ if (limit == 0)
+ return result;
+
+ if (RegExpObject* re = separatorValue.as<RegExpObject>()) {
+ if (re->value->pattern().isEmpty()) {
+ re = 0;
+ separatorValue = Value::fromString(ctx, QString());
+ }
+ }
+
+ if (RegExpObject* re = separatorValue.as<RegExpObject>()) {
+ uint offset = 0;
+ uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint));
+ while (true) {
+ uint result = re->value->match(text, offset, matchOffsets);
+ if (result == JSC::Yarr::offsetNoMatch)
+ break;
+
+ array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset)));
+ offset = qMax(offset + 1, matchOffsets[1]);
+
+ if (array->arrayLength() >= limit)
+ break;
+
+ for (int i = 1; i < re->value->captureCount(); ++i) {
+ uint start = matchOffsets[i * 2];
+ uint end = matchOffsets[i * 2 + 1];
+ array->push_back(Value::fromString(ctx, text.mid(start, end - start)));
+ if (array->arrayLength() >= limit)
+ break;
+ }
+ }
+ if (array->arrayLength() < limit)
+ array->push_back(Value::fromString(ctx, text.mid(offset)));
+ } else {
+ QString separator = separatorValue.toString(ctx)->toQString();
+ if (separator.isEmpty()) {
+ for (uint i = 0; i < qMin(limit, uint(text.length())); ++i)
+ array->push_back(Value::fromString(ctx, text.mid(i, 1)));
+ return result;
+ }
+
+ int start = 0;
+ int end;
+ while ((end = text.indexOf(separator, start)) != -1) {
+ array->push_back(Value::fromString(ctx, text.mid(start, end - start)));
+ start = end + separator.size();
+ if (array->arrayLength() >= limit)
+ break;
+ }
+ if (array->arrayLength() < limit && start != -1)
+ array->push_back(Value::fromString(ctx, text.mid(start)));
+ }
+ return result;
+}
+
+Value StringPrototype::method_substr(SimpleCallContext *context)
+{
+ const QString value = getThisString(context, context->thisObject);
+
+ double start = 0;
+ if (context->argumentCount > 0)
+ start = context->arguments[0].toInteger();
+
+ double length = +qInf();
+ if (context->argumentCount > 1)
+ length = context->arguments[1].toInteger();
+
+ double count = value.length();
+ if (start < 0)
+ start = qMax(count + start, 0.0);
+
+ length = qMin(qMax(length, 0.0), count - start);
+
+ qint32 x = Value::toInt32(start);
+ qint32 y = Value::toInt32(length);
+ return Value::fromString(context, value.mid(x, y));
+}
+
+Value StringPrototype::method_substring(SimpleCallContext *context)
+{
+ QString value = getThisString(context, context->thisObject);
+ int length = value.length();
+
+ double start = 0;
+ double end = length;
+
+ if (context->argumentCount > 0)
+ start = context->arguments[0].toInteger();
+
+ Value endValue = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue();
+ if (!endValue.isUndefined())
+ end = endValue.toInteger();
+
+ if (std::isnan(start) || start < 0)
+ start = 0;
+
+ if (std::isnan(end) || end < 0)
+ end = 0;
+
+ if (start > length)
+ start = length;
+
+ if (end > length)
+ end = length;
+
+ if (start > end) {
+ double was = start;
+ start = end;
+ end = was;
+ }
+
+ qint32 x = (int)start;
+ qint32 y = (int)(end - start);
+ return Value::fromString(context, value.mid(x, y));
+}
+
+Value StringPrototype::method_toLowerCase(SimpleCallContext *ctx)
+{
+ QString value = getThisString(ctx);
+ return Value::fromString(ctx, value.toLower());
+}
+
+Value StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx)
+{
+ return method_toLowerCase(ctx);
+}
+
+Value StringPrototype::method_toUpperCase(SimpleCallContext *ctx)
+{
+ QString value = getThisString(ctx);
+ return Value::fromString(ctx, value.toUpper());
+}
+
+Value StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx)
+{
+ return method_toUpperCase(ctx);
+}
+
+Value StringPrototype::method_fromCharCode(SimpleCallContext *context)
+{
+ QString str(context->argumentCount, Qt::Uninitialized);
+ QChar *ch = str.data();
+ for (int i = 0; i < context->argumentCount; ++i) {
+ *ch = QChar(context->arguments[i].toUInt16());
+ ++ch;
+ }
+ return Value::fromString(context, str);
+}
+
+Value StringPrototype::method_trim(SimpleCallContext *ctx)
+{
+ if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined())
+ ctx->throwTypeError();
+
+ QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString();
+ const QChar *chars = s.constData();
+ int start, end;
+ for (start = 0; start < s.length(); ++start) {
+ if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff)
+ break;
+ }
+ for (end = s.length() - 1; end >= start; --end) {
+ if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff)
+ break;
+ }
+
+ return Value::fromString(ctx, QString(chars + start, end - start + 1));
+}
diff --git a/src/qml/qml/v4/qv4stringobject_p.h b/src/qml/qml/v4/qv4stringobject_p.h
new file mode 100644
index 0000000000..0ef6596235
--- /dev/null
+++ b/src/qml/qml/v4/qv4stringobject_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STRINGOBJECT_P_H
+#define QV4STRINGOBJECT_P_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct StringObject: Object {
+ Q_MANAGED
+
+ Value value;
+ mutable Property tmpProperty;
+ StringObject(ExecutionEngine *engine, const Value &value);
+
+ Property *getIndex(uint index) const;
+
+ static bool deleteIndexedProperty(Managed *m, uint index);
+
+protected:
+ static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs);
+ static void markObjects(Managed *that);
+};
+
+struct StringCtor: FunctionObject
+{
+ StringCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *m, Value *args, int argc);
+ static Value call(Managed *that, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct StringPrototype: StringObject
+{
+ StringPrototype(ExecutionEngine *engine): StringObject(engine, Value::fromString(engine, QString())) {}
+ void init(ExecutionEngine *engine, const Value &ctor);
+
+ static Value method_toString(SimpleCallContext *context);
+ static Value method_charAt(SimpleCallContext *context);
+ static Value method_charCodeAt(SimpleCallContext *context);
+ static Value method_concat(SimpleCallContext *context);
+ static Value method_indexOf(SimpleCallContext *context);
+ static Value method_lastIndexOf(SimpleCallContext *context);
+ static Value method_localeCompare(SimpleCallContext *context);
+ static Value method_match(SimpleCallContext *context);
+ static Value method_replace(SimpleCallContext *ctx);
+ static Value method_search(SimpleCallContext *ctx);
+ static Value method_slice(SimpleCallContext *ctx);
+ static Value method_split(SimpleCallContext *ctx);
+ static Value method_substr(SimpleCallContext *context);
+ static Value method_substring(SimpleCallContext *context);
+ static Value method_toLowerCase(SimpleCallContext *ctx);
+ static Value method_toLocaleLowerCase(SimpleCallContext *ctx);
+ static Value method_toUpperCase(SimpleCallContext *ctx);
+ static Value method_toLocaleUpperCase(SimpleCallContext *ctx);
+ static Value method_fromCharCode(SimpleCallContext *context);
+ static Value method_trim(SimpleCallContext *ctx);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4/qv4syntaxchecker.cpp b/src/qml/qml/v4/qv4syntaxchecker.cpp
new file mode 100644
index 0000000000..a4f7423cd7
--- /dev/null
+++ b/src/qml/qml/v4/qv4syntaxchecker.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4syntaxchecker_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+
+SyntaxChecker::SyntaxChecker()
+ : Lexer(&m_engine)
+ , m_stateStack(128)
+{
+}
+
+void QQmlJS::SyntaxChecker::clearText()
+{
+ m_code.clear();
+ m_tokens.clear();
+}
+
+void SyntaxChecker::appendText(const QString &text)
+{
+ m_code += text;
+}
+
+QString SyntaxChecker::text() const
+{
+ return m_code;
+}
+
+bool SyntaxChecker::canEvaluate()
+{
+ int yyaction = 0;
+ int yytoken = -1;
+ int yytos = -1;
+
+ setCode(m_code, 1);
+
+ m_tokens.clear();
+ m_tokens.append(T_FEED_JS_PROGRAM);
+
+ do {
+ if (++yytos == m_stateStack.size())
+ m_stateStack.resize(m_stateStack.size() * 2);
+
+ m_stateStack[yytos] = yyaction;
+
+again:
+ if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) {
+ if (m_tokens.isEmpty())
+ yytoken = lex();
+ else
+ yytoken = m_tokens.takeFirst();
+ }
+
+ yyaction = t_action(yyaction, yytoken);
+ if (yyaction > 0) {
+ if (yyaction == ACCEPT_STATE) {
+ --yytos;
+ return true;
+ }
+ yytoken = -1;
+ } else if (yyaction < 0) {
+ const int ruleno = -yyaction - 1;
+ yytos -= rhs[ruleno];
+ yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT);
+ }
+ } while (yyaction);
+
+ const int errorState = m_stateStack[yytos];
+ if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) {
+ yyaction = errorState;
+ m_tokens.prepend(yytoken);
+ yytoken = T_SEMICOLON;
+ goto again;
+ }
+
+ if (yytoken != EOF_SYMBOL)
+ return true;
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4syntaxchecker_p.h b/src/qml/qml/v4/qv4syntaxchecker_p.h
new file mode 100644
index 0000000000..bdb88b0525
--- /dev/null
+++ b/src/qml/qml/v4/qv4syntaxchecker_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4SYNTAXCHECKER_P_H
+#define QV4SYNTAXCHECKER_P_H
+
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsengine_p.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+class SyntaxChecker: Lexer
+{
+public:
+ SyntaxChecker();
+
+ QString text() const;
+ void clearText();
+ void appendText(const QString &text);
+
+ bool canEvaluate();
+
+private:
+ Engine m_engine;
+ QVector<int> m_stateStack;
+ QList<int> m_tokens;
+ QString m_code;
+};
+
+} // end of QQmlJS namespace
+
+QT_END_NAMESPACE
+
+#endif // QV4SYNTAXCHECKER_P_H
diff --git a/src/qml/qml/v4/qv4unwindhelper.cpp b/src/qml/qml/v4/qv4unwindhelper.cpp
new file mode 100644
index 0000000000..beb5132626
--- /dev/null
+++ b/src/qml/qml/v4/qv4unwindhelper.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4unwindhelper_p.h>
+
+#include <wtf/Platform.h>
+
+#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X))
+# define USE_DW2_HELPER
+#elif CPU(X86) && COMPILER(GCC)
+# define USE_DW2_HELPER
+#elif CPU(ARM) && (OS(LINUX) || OS(QNX))
+# define USE_ARM_HELPER
+#elif OS(WINDOWS)
+ // SJLJ will unwind on Windows
+# define USE_NULL_HELPER
+#elif OS(IOS)
+ // SJLJ will unwind on iOS
+# define USE_NULL_HELPER
+#else
+# warning "Unsupported/untested platform!"
+# define USE_NULL_HELPER
+#endif
+
+#ifdef USE_DW2_HELPER
+# include <qv4unwindhelper_p-dw2.h>
+#endif // USE_DW2_HELPER
+
+#ifdef USE_ARM_HELPER
+# include <qv4unwindhelper_p-arm.h>
+#endif // USE_ARM_HELPER
+
+QT_BEGIN_NAMESPACE
+
+#ifdef USE_NULL_HELPER
+using namespace QV4;
+void UnwindHelper::prepareForUnwind(ExecutionContext *) {}
+void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);}
+void UnwindHelper::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);}
+void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);}
+void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);}
+#endif // USE_NULL_HELPER
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4unwindhelper_p-arm.h b/src/qml/qml/v4/qv4unwindhelper_p-arm.h
new file mode 100644
index 0000000000..dd1f1e4856
--- /dev/null
+++ b/src/qml/qml/v4/qv4unwindhelper_p-arm.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4UNWINDHELPER_PDW2_H
+#define QV4UNWINDHELPER_PDW2_H
+
+#include "qv4unwindhelper_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+#include <wtf/Platform.h>
+
+#include <QMap>
+#include <QMutex>
+
+#define __USE_GNU
+#include <dlfcn.h>
+
+#if USE(LIBUNWIND_DEBUG)
+#include <libunwind.h>
+#include <execinfo.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+static void *removeThumbBit(void *addr)
+{
+ return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(addr) & ~1u);
+}
+
+static QMutex functionProtector;
+static QMap<quintptr, Function*> allFunctions;
+
+static Function *lookupFunction(void *pc)
+{
+ quintptr key = reinterpret_cast<quintptr>(pc);
+ QMap<quintptr, Function*>::ConstIterator it = allFunctions.lowerBound(key);
+ if (it != allFunctions.begin() && allFunctions.count() > 0)
+ --it;
+ if (it == allFunctions.end())
+ return 0;
+
+ quintptr codeStart = reinterpret_cast<quintptr>(removeThumbBit((*it)->codeRef.code().executableAddress()));
+ if (key < codeStart || key >= codeStart + (*it)->codeSize)
+ return 0;
+ return *it;
+}
+
+
+/* Program:
+vsp = r4 (REG_TO_SP r4)
+vsp -= 8 * 4 -- > vsp = vsp - (7 << 2) - 4
+pop r12, r10, r9, r8, r7, r6, r5, r4
+pop r4
+pop lr
+pop r0, r1, r2, r3
+*/
+
+#define REG_TO_SP 0b10010000
+#define VSP_MINUS 0b01000000
+#define POP_REG_MULTI 0b10000000
+#define POP_R4_MULTI 0b10100000
+#define POP_R4_R14_MULTI 0b10101000
+#define POP_R0_TO_R3 0b10110001
+#define FINISH 0b10110000
+
+#define MK_UW_WORD(first, second, third, fourth) \
+ (((first) << 24) | \
+ ((second) << 16) | \
+ ((third) << 8) | \
+ (fourth))
+
+static unsigned int extbl[] = {
+ MK_UW_WORD(0x80 | // High bit set to indicate that this isn't a PREL31
+ 2, // Choose personality routine #2
+ 2, // Number of 4 byte words used to encode remaining unwind instructions
+ REG_TO_SP | 4, // Encoded program from above.
+ VSP_MINUS | 7),
+ MK_UW_WORD(POP_REG_MULTI | 1, 0b01111111,
+ POP_R4_R14_MULTI,
+ POP_R0_TO_R3),
+ MK_UW_WORD(0b00001111,
+ FINISH,
+ FINISH,
+ FINISH)
+};
+
+static unsigned write_prel31(unsigned *addr, void *ptr)
+{
+ int delta = (char *)ptr - (char*)addr;
+ if (delta < 0)
+ delta |= (1 << 30);
+ else
+ delta &= ~(1 << 30);
+ *addr = ((unsigned)delta) & 0x7fffffffU;
+}
+
+void UnwindHelper::deregisterFunction(Function *function)
+{
+ QMutexLocker locker(&functionProtector);
+ allFunctions.remove(reinterpret_cast<quintptr>(function->code));
+}
+
+void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions)
+{
+ QMutexLocker locker(&functionProtector);
+ foreach (Function *f, functions)
+ allFunctions.remove(reinterpret_cast<quintptr>(f->code));
+}
+
+void UnwindHelper::registerFunction(Function *function)
+{
+ QMutexLocker locker(&functionProtector);
+ allFunctions.insert(reinterpret_cast<quintptr>(function->code), function);
+}
+
+void UnwindHelper::registerFunctions(const QVector<Function *> &functions)
+{
+ QMutexLocker locker(&functionProtector);
+ foreach (Function *f, functions)
+ allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
+}
+
+void UnwindHelper::prepareForUnwind(ExecutionContext *)
+{
+}
+
+int UnwindHelper::unwindInfoSize()
+{
+ return 2 * sizeof(unsigned int) // 2 extbl entries
+ + sizeof(extbl);
+}
+
+void UnwindHelper::writeARMUnwindInfo(void *codeAddr, int codeSize)
+{
+ unsigned int *exidx = (unsigned int *)((char *)codeAddr + codeSize);
+
+ unsigned char *exprog = (unsigned char *)((unsigned char *)codeAddr + codeSize + 8);
+
+ write_prel31(exidx, codeAddr);
+ exidx[1] = 4; // PREL31 offset to extbl, which follows right afterwards
+
+ memcpy(exprog, extbl, sizeof(extbl));
+
+#if USE(LIBUNWIND_DEBUG)
+ unw_dyn_info_t *info = (unw_dyn_info_t*)malloc(sizeof(unw_dyn_info_t));
+ info->start_ip = (unw_word_t)codeAddr;
+ info->end_ip = info->start_ip + codeSize;
+ info->gp = 0;
+ info->format = UNW_INFO_FORMAT_ARM_EXIDX;
+ info->u.rti.name_ptr = 0;
+ info->u.rti.segbase = 0;
+ info->u.rti.table_len = 8;
+ info->u.rti.table_data = (unw_word_t)exidx;
+ _U_dyn_register(info);
+#endif
+}
+
+}
+
+QT_END_NAMESPACE
+
+#if defined(Q_OS_ANDROID)
+extern "C" void *dl_unwind_find_exidx(void *pc, int *entryCount);
+#endif
+
+extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount)
+{
+#if !defined(Q_OS_ANDROID)
+ typedef void *(*Old_Unwind_Find_exidx)(void*, int*);
+ static Old_Unwind_Find_exidx oldFunction = 0;
+ if (!oldFunction)
+ oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx");
+#endif
+
+ {
+ QMutexLocker locker(&QT_PREPEND_NAMESPACE(QV4::functionProtector));
+ QV4::Function *function = QT_PREPEND_NAMESPACE(QV4::lookupFunction(pc));
+ if (function) {
+ *entryCount = 1;
+ void * codeStart = QT_PREPEND_NAMESPACE(QV4::removeThumbBit(function->codeRef.code().executableAddress()));
+ // At the end of the function we store our synthetic exception table entry.
+ return (char *)codeStart + function->codeSize;
+ }
+ }
+
+#if defined(Q_OS_ANDROID)
+ return dl_unwind_find_exidx(pc, entryCount);
+#else
+ return oldFunction(pc, entryCount);
+#endif
+}
+
+#endif // QV4UNWINDHELPER_PDW2_H
diff --git a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h
new file mode 100644
index 0000000000..57615f0999
--- /dev/null
+++ b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4UNWINDHELPER_PDW2_H
+#define QV4UNWINDHELPER_PDW2_H
+
+#include "qv4unwindhelper_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4function_p.h"
+#include <wtf/Platform.h>
+#include <wtf/PageAllocation.h>
+#include <ExecutableAllocator.h>
+
+#include <QMap>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace {
+#if CPU(X86_64)
+// Generated by fdegen
+static const unsigned char cie_fde_data[] = {
+ 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8,
+ 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
+ 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86,
+ 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4,
+ 0x0, 0x0, 0x0, 0x0
+};
+static const int fde_offset = 20;
+static const int initial_location_offset = 28;
+static const int address_range_offset = 36;
+#elif CPU(X86)
+static const unsigned char cie_fde_data[] = {
+ 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4,
+ 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
+ 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85,
+ 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4,
+ 0x0, 0x0, 0x0, 0x0,
+};
+static const int fde_offset = 20;
+static const int initial_location_offset = 28;
+static const int address_range_offset = 32;
+#endif
+
+void writeIntPtrValue(unsigned char *addr, intptr_t val)
+{
+ addr[0] = (val >> 0) & 0xff;
+ addr[1] = (val >> 8) & 0xff;
+ addr[2] = (val >> 16) & 0xff;
+ addr[3] = (val >> 24) & 0xff;
+#if QT_POINTER_SIZE == 8
+ addr[4] = (val >> 32) & 0xff;
+ addr[5] = (val >> 40) & 0xff;
+ addr[6] = (val >> 48) & 0xff;
+ addr[7] = (val >> 56) & 0xff;
+#endif
+}
+} // anonymous namespace
+
+extern "C" void __register_frame(void *fde);
+extern "C" void __deregister_frame(void *fde);
+
+struct UnwindInfo : public ExecutableAllocator::PlatformUnwindInfo
+{
+ UnwindInfo(const QByteArray &cieFde);
+ virtual ~UnwindInfo();
+ QByteArray data;
+};
+
+UnwindInfo::UnwindInfo(const QByteArray &cieFde)
+ : data(cieFde)
+{
+ __register_frame(data.data() + fde_offset);
+}
+
+UnwindInfo::~UnwindInfo()
+{
+ __deregister_frame(data.data() + fde_offset);
+}
+
+static void ensureUnwindInfo(Function *f)
+{
+ if (!f->codeRef)
+ return; // Not a JIT generated function
+
+ JSC::ExecutableMemoryHandle *handle = f->codeRef.executableMemory();
+ if (!handle)
+ return;
+ ExecutableAllocator::ChunkOfPages *chunk = handle->chunk();
+
+ // Already registered?
+ if (chunk->unwindInfo)
+ return;
+
+ QByteArray info;
+ info.resize(sizeof(cie_fde_data));
+
+ unsigned char *cie_and_fde = reinterpret_cast<unsigned char *>(info.data());
+ memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data));
+
+ intptr_t ptr = static_cast<char *>(chunk->pages->base()) - static_cast<char *>(0);
+ writeIntPtrValue(cie_and_fde + initial_location_offset, ptr);
+
+ writeIntPtrValue(cie_and_fde + address_range_offset, chunk->pages->size());
+
+ chunk->unwindInfo = new UnwindInfo(info);
+}
+
+void UnwindHelper::prepareForUnwind(ExecutionContext *context)
+{
+ for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) {
+ if (CallContext *callCtx = ctx->asCallContext())
+ if (FunctionObject *fobj = callCtx->function)
+ if (Function *fun = fobj->function)
+ ensureUnwindInfo(fun);
+ for (ExecutionContext::EvalCode *code = ctx->currentEvalCode;
+ code; code = code->next)
+ ensureUnwindInfo(code->function);
+ }
+
+ if (context->engine->globalCode)
+ ensureUnwindInfo(context->engine->globalCode);
+}
+
+void UnwindHelper::registerFunction(Function *)
+{
+}
+
+void UnwindHelper::registerFunctions(const QVector<Function *>&)
+{
+}
+
+void UnwindHelper::deregisterFunction(Function *)
+{
+}
+
+void UnwindHelper::deregisterFunctions(const QVector<Function *> &)
+{
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4UNWINDHELPER_PDW2_H
diff --git a/src/qml/qml/v4/qv4unwindhelper_p.h b/src/qml/qml/v4/qv4unwindhelper_p.h
new file mode 100644
index 0000000000..9ef564449a
--- /dev/null
+++ b/src/qml/qml/v4/qv4unwindhelper_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4UNWINDHELPER_H
+#define QV4UNWINDHELPER_H
+
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+struct ExecutionContext;
+
+class UnwindHelper
+{
+public:
+ static void prepareForUnwind(ExecutionContext *ctx);
+ static void registerFunction(Function *function);
+ static void registerFunctions(const QVector<Function *> &functions);
+ static void deregisterFunction(Function *function);
+ static void deregisterFunctions(const QVector<Function *> &functions);
+#ifdef Q_PROCESSOR_ARM
+ static int unwindInfoSize();
+ static void writeARMUnwindInfo(void *codeAddr, int codeSize);
+#endif
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4UNWINDHELPER_H
diff --git a/src/qml/qml/v4/qv4util_p.h b/src/qml/qml/v4/qv4util_p.h
new file mode 100644
index 0000000000..dbd9f89faa
--- /dev/null
+++ b/src/qml/qml/v4/qv4util_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4UTIL_H
+#define QV4UTIL_H
+
+#include "qv4global_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+template <typename T>
+struct TemporaryAssignment
+{
+ TemporaryAssignment(T &var, const T& temporaryValue)
+ : variable(var)
+ , savedValue(var)
+ {
+ variable = temporaryValue;
+ }
+ ~TemporaryAssignment()
+ {
+ variable = savedValue;
+ }
+ T &variable;
+ T savedValue;
+private:
+ TemporaryAssignment(const TemporaryAssignment<T>&);
+ TemporaryAssignment operator=(const TemporaryAssignment<T>&);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4UTIL_H
diff --git a/src/qml/qml/v4/qv4value.cpp b/src/qml/qml/v4/qv4value.cpp
new file mode 100644
index 0000000000..a41262f12f
--- /dev/null
+++ b/src/qml/qml/v4/qv4value.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4engine_p.h>
+#include <qv4object_p.h>
+#include <qv4objectproto_p.h>
+#include "qv4mm_p.h"
+#include "qv4exception_p.h"
+
+#include <wtf/MathExtras.h>
+
+using namespace QV4;
+
+int Value::toUInt16() const
+{
+ if (isConvertibleToInt())
+ return (ushort)(uint)integerValue();
+
+ double number = __qmljs_to_number(*this);
+
+ double D16 = 65536.0;
+ if ((number >= 0 && number < D16))
+ return static_cast<ushort>(number);
+
+ if (!std::isfinite(number))
+ return +0;
+
+ double d = ::floor(::fabs(number));
+ if (std::signbit(number))
+ d = -d;
+
+ number = ::fmod(d , D16);
+
+ if (number < 0)
+ number += D16;
+
+ return (unsigned short)number;
+}
+
+double Value::toInteger() const
+{
+ if (isConvertibleToInt())
+ return int_32;
+
+ return Value::toInteger(__qmljs_to_number(*this));
+}
+
+double Value::toNumber() const
+{
+ return __qmljs_to_number(*this);
+}
+
+QString Value::toQString() const
+{
+ switch (type()) {
+ case Value::Undefined_Type:
+ return QStringLiteral("undefined");
+ case Value::Null_Type:
+ return QStringLiteral("null");
+ case Value::Boolean_Type:
+ if (booleanValue())
+ return QStringLiteral("true");
+ else
+ return QStringLiteral("false");
+ case Value::String_Type:
+ return stringValue()->toQString();
+ case Value::Object_Type: {
+ ExecutionContext *ctx = objectValue()->internalClass->engine->current;
+ try {
+ Value prim = __qmljs_to_primitive(*this, STRING_HINT);
+ if (prim.isPrimitive())
+ return prim.toQString();
+ } catch (Exception &e) {
+ e.accept(ctx);
+ try {
+ Value prim = __qmljs_to_primitive(e.value(), STRING_HINT);
+ if (prim.isPrimitive())
+ return prim.toQString();
+ } catch(Exception &e) {
+ e.accept(ctx);
+ }
+ }
+ return QString();
+ }
+ case Value::Integer_Type: {
+ QString str;
+ __qmljs_numberToString(&str, (double)int_32, 10);
+ return str;
+ }
+ default: { // double
+ QString str;
+ __qmljs_numberToString(&str, doubleValue(), 10);
+ return str;
+ }
+ } // switch
+}
+
+bool Value::sameValue(Value other) const {
+ if (val == other.val)
+ return true;
+ if (isString() && other.isString())
+ return stringValue()->isEqualTo(other.stringValue());
+ if (isInteger())
+ return int_32 ? (double(int_32) == other.dbl) : (other.val == 0);
+ if (other.isInteger())
+ return other.int_32 ? (dbl == double(other.int_32)) : (val == 0);
+ return false;
+}
+
+Value Value::fromString(ExecutionContext *ctx, const QString &s)
+{
+ return fromString(ctx->engine->newString(s));
+}
+
+Value Value::fromString(ExecutionEngine *engine, const QString &s)
+{
+ return fromString(engine->newString(s));
+}
+
+
+int Value::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 Value::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 Value::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;
+}
+
+String *Value::toString(ExecutionContext *ctx) const
+{
+ if (isString())
+ return stringValue();
+ return __qmljs_convert_to_string(ctx, *this);
+}
+
+Value Value::property(ExecutionContext *ctx, String *name) const
+{
+ return isObject() ? objectValue()->get(name) : undefinedValue();
+}
+
+
+PersistentValue::PersistentValue(const Value &val)
+ : d(new PersistentValuePrivate(val))
+{
+}
+
+PersistentValue::PersistentValue(const PersistentValue &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref();
+}
+
+PersistentValue &PersistentValue::operator=(const PersistentValue &other)
+{
+ if (d == other.d)
+ return *this;
+
+ // the memory manager cleans up those with a refcount of 0
+
+ if (d)
+ d->deref();
+ d = other.d;
+ if (d)
+ d->ref();
+
+ return *this;
+}
+
+PersistentValue &PersistentValue::operator =(const Value &other)
+{
+ if (!d) {
+ d = new PersistentValuePrivate(other);
+ return *this;
+ }
+ d = d->detach(other);
+ return *this;
+}
+
+PersistentValue::~PersistentValue()
+{
+ if (d)
+ d->deref();
+}
+
+WeakValue::WeakValue(const Value &val)
+ : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true))
+{
+}
+
+WeakValue::WeakValue(const WeakValue &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref();
+}
+
+WeakValue &WeakValue::operator=(const WeakValue &other)
+{
+ if (d == other.d)
+ return *this;
+
+ // the memory manager cleans up those with a refcount of 0
+
+ if (d)
+ d->deref();
+ d = other.d;
+ if (d)
+ d->ref();
+
+ return *this;
+}
+
+WeakValue &WeakValue::operator =(const Value &other)
+{
+ if (!d) {
+ d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true);
+ return *this;
+ }
+ d = d->detach(other, /*weak*/true);
+ return *this;
+}
+
+
+WeakValue::~WeakValue()
+{
+ if (d)
+ d->deref();
+}
+
+void WeakValue::markOnce()
+{
+ if (!d)
+ return;
+ Managed *m = d->value.asManaged();
+ if (!m)
+ return;
+ m->mark();
+}
+
+PersistentValuePrivate::PersistentValuePrivate(const Value &v, ExecutionEngine *e, bool weak)
+ : value(v)
+ , refcount(1)
+ , prev(0)
+ , next(0)
+ , engine(e)
+{
+ if (!engine) {
+ Managed *m = v.asManaged();
+ if (!m)
+ return;
+
+ engine = m->engine();
+ }
+ if (engine) {
+ PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues;
+
+ prev = listRoot;
+ next = *listRoot;
+ *prev = this;
+ if (next)
+ next->prev = &this->next;
+ }
+}
+
+PersistentValuePrivate::~PersistentValuePrivate()
+{
+}
+
+void PersistentValuePrivate::removeFromList()
+{
+ if (prev) {
+ if (next)
+ next->prev = prev;
+ *prev = next;
+ next = 0;
+ prev = 0;
+ }
+}
+
+void PersistentValuePrivate::deref()
+{
+ // if engine is not 0, they are registered with the memory manager
+ // and will get cleaned up in the next gc run
+ if (!--refcount) {
+ removeFromList();
+ delete this;
+ }
+}
+
+PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::Value &value, bool weak)
+{
+ if (refcount == 1) {
+ this->value = value;
+
+ Managed *m = value.asManaged();
+ if (!prev) {
+ if (m) {
+ ExecutionEngine *engine = m->engine();
+ if (engine) {
+ PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues;
+ prev = listRoot;
+ next = *listRoot;
+ *prev = this;
+ if (next)
+ next->prev = &this->next;
+ }
+ }
+ } else if (!m)
+ removeFromList();
+
+ return this;
+ }
+ --refcount;
+ return new PersistentValuePrivate(value, engine, weak);
+}
+
diff --git a/src/qml/qml/v4/qv4value_def_p.h b/src/qml/qml/v4/qv4value_def_p.h
new file mode 100644
index 0000000000..2e3a9c0425
--- /dev/null
+++ b/src/qml/qml/v4/qv4value_def_p.h
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4VALUE_DEF_P_H
+#define QV4VALUE_DEF_P_H
+
+#include <QtCore/QString>
+#include "qv4global_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+typedef uint Bool;
+
+struct Q_QML_EXPORT Value
+{
+ union {
+ quint64 val;
+ double dbl;
+ struct {
+#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN
+ uint tag;
+#endif
+ union {
+ uint uint_32;
+ int int_32;
+#if QT_POINTER_SIZE == 4
+ Managed *m;
+ Object *o;
+ String *s;
+#endif
+ };
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint tag;
+#endif
+ };
+ };
+
+ enum Masks {
+ NotDouble_Mask = 0xfffc0000,
+ Type_Mask = 0xffff8000,
+ Immediate_Mask = NotDouble_Mask | 0x00008000,
+ Special_Mask = Immediate_Mask | 0x20000,
+ Tag_Shift = 32
+ };
+ enum ValueType {
+ Undefined_Type = Immediate_Mask | 0x00000,
+ Null_Type = Immediate_Mask | 0x10000,
+ Boolean_Type = Immediate_Mask | 0x20000,
+ Integer_Type = Immediate_Mask | 0x30000,
+ Object_Type = NotDouble_Mask | 0x00000,
+ String_Type = NotDouble_Mask | 0x10000,
+ Deleted_Type = NotDouble_Mask | 0x30000
+ };
+
+ enum ImmediateFlags {
+ ConvertibleToInt = Immediate_Mask | 0x1
+ };
+
+ enum ValueTypeInternal {
+ _Undefined_Type = Undefined_Type,
+ _Empty_Type = Deleted_Type,
+ _Null_Type = Null_Type | ConvertibleToInt,
+ _Boolean_Type = Boolean_Type | ConvertibleToInt,
+ _Integer_Type = Integer_Type | ConvertibleToInt,
+ _Object_Type = Object_Type,
+ _String_Type = String_Type
+
+ };
+
+ inline unsigned type() const {
+ return tag & Type_Mask;
+ }
+
+ // used internally in property
+ inline bool isEmpty() const { return tag == _Empty_Type; }
+
+ inline bool isUndefined() const { return tag == _Undefined_Type; }
+ inline bool isNull() const { return tag == _Null_Type; }
+ inline bool isBoolean() const { return tag == _Boolean_Type; }
+ inline bool isInteger() const { return tag == _Integer_Type; }
+ inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; }
+ inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; }
+#if QT_POINTER_SIZE == 8
+ inline bool isString() const { return (tag & Type_Mask) == String_Type; }
+ inline bool isObject() const { return (tag & Type_Mask) == Object_Type; }
+#else
+ inline bool isString() const { return tag == String_Type; }
+ inline bool isObject() const { return tag == Object_Type; }
+#endif
+ inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; }
+ inline bool isInt32() {
+ if (tag == _Integer_Type)
+ return true;
+ if (isDouble()) {
+ int i = (int)dbl;
+ if (i == dbl) {
+ int_32 = i;
+ tag = _Integer_Type;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool booleanValue() const {
+ return int_32;
+ }
+ double doubleValue() const {
+ return dbl;
+ }
+ void setDouble(double d) {
+ dbl = d;
+ }
+ double asDouble() const {
+ if (tag == _Integer_Type)
+ return int_32;
+ return dbl;
+ }
+ int integerValue() const {
+ return int_32;
+ }
+
+#if QT_POINTER_SIZE == 8
+ String *stringValue() const {
+ return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift));
+ }
+ Object *objectValue() const {
+ return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift));
+ }
+ Managed *managed() const {
+ return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift));
+ }
+#else
+ String *stringValue() const {
+ return s;
+ }
+ Object *objectValue() const {
+ return o;
+ }
+ Managed *managed() const {
+ return m;
+ }
+#endif
+
+ quint64 rawValue() const {
+ return val;
+ }
+
+ static Value emptyValue();
+ static Value undefinedValue();
+ static Value nullValue();
+ static Value fromBoolean(Bool b);
+ static Value fromDouble(double d);
+ static Value fromInt32(int i);
+ static Value fromUInt32(uint i);
+ static Value fromString(String *s);
+ static Value fromObject(Object *o);
+
+#ifndef QMLJS_LLVM_RUNTIME
+ static Value fromString(ExecutionContext *ctx, const QString &fromString);
+ static Value fromString(ExecutionEngine *engine, const QString &s);
+#endif
+
+ static double toInteger(double fromNumber);
+ static int toInt32(double value);
+ static unsigned int toUInt32(double value);
+
+ int toUInt16() const;
+ int toInt32() const;
+ unsigned int toUInt32() const;
+
+ bool toBoolean() const;
+ double toInteger() const;
+ double toNumber() const;
+ QString toQString() const;
+ String *toString(ExecutionContext *ctx) const;
+ Object *toObject(ExecutionContext *ctx) const;
+
+ inline bool isPrimitive() const { return !isObject(); }
+#if QT_POINTER_SIZE == 8
+ inline bool integerCompatible() const {
+ const quint64 mask = quint64(ConvertibleToInt) << 32;
+ return (val & mask) == mask;
+ }
+ static inline bool integerCompatible(Value a, Value b) {
+ const quint64 mask = quint64(ConvertibleToInt) << 32;
+ return ((a.val & b.val) & mask) == mask;
+ }
+ static inline bool bothDouble(Value a, Value b) {
+ const quint64 mask = quint64(NotDouble_Mask) << 32;
+ return ((a.val | b.val) & mask) != mask;
+ }
+#else
+ inline bool integerCompatible() const {
+ return (tag & ConvertibleToInt) == ConvertibleToInt;
+ }
+ static inline bool integerCompatible(Value a, Value b) {
+ return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt;
+ }
+ static inline bool bothDouble(Value a, Value b) {
+ return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask;
+ }
+#endif
+ inline bool tryIntegerConversion() {
+ bool b = isConvertibleToInt();
+ if (b)
+ tag = _Integer_Type;
+ return b;
+ }
+
+ String *asString() const;
+ Managed *asManaged() const;
+ Object *asObject() const;
+ FunctionObject *asFunctionObject() const;
+ BooleanObject *asBooleanObject() const;
+ NumberObject *asNumberObject() const;
+ StringObject *asStringObject() const;
+ DateObject *asDateObject() const;
+ ArrayObject *asArrayObject() const;
+ ErrorObject *asErrorObject() const;
+
+ template<typename T> inline T *as() const;
+
+ uint asArrayIndex() const;
+ uint asArrayLength(bool *ok) const;
+
+ Value property(ExecutionContext *ctx, String *name) const;
+
+ inline ExecutionEngine *engine() const;
+
+ // Section 9.12
+ bool sameValue(Value other) const;
+
+ inline void mark() const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4VALUE_DEF_P_H
diff --git a/src/qml/qml/v4/qv4value_p.h b/src/qml/qml/v4/qv4value_p.h
new file mode 100644
index 0000000000..6d3f0e6edc
--- /dev/null
+++ b/src/qml/qml/v4/qv4value_p.h
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QMLJS_VALUE_H
+#define QMLJS_VALUE_H
+
+#include <cmath> // this HAS to come
+
+#include <QtCore/QString>
+#include <QtCore/qnumeric.h>
+#include "qv4global_p.h"
+#include "qv4string_p.h"
+#include <QtCore/QDebug>
+#include "qv4managed_p.h"
+
+//#include <wtf/MathExtras.h>
+
+#include "qv4value_def_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+double __qmljs_to_number(const QV4::Value &value);
+Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value);
+QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value);
+
+
+inline ExecutionEngine *Value::engine() const {
+ Managed *m = asManaged();
+ return m ? m->engine() : 0;
+}
+
+inline void Value::mark() const {
+ Managed *m = asManaged();
+ if (m)
+ m->mark();
+}
+
+inline Value Value::undefinedValue()
+{
+ Value v;
+#if QT_POINTER_SIZE == 8
+ v.val = quint64(_Undefined_Type) << Tag_Shift;
+#else
+ v.tag = _Undefined_Type;
+ v.int_32 = 0;
+#endif
+ return v;
+}
+
+inline Value Value::nullValue()
+{
+ Value v;
+#if QT_POINTER_SIZE == 8
+ v.val = quint64(_Null_Type) << Tag_Shift;
+#else
+ v.tag = _Null_Type;
+ v.int_32 = 0;
+#endif
+ return v;
+}
+
+inline Value Value::emptyValue()
+{
+ Value v;
+ v.tag = Value::_Empty_Type;
+ v.uint_32 = 0;
+ return v;
+}
+
+
+inline Value Value::fromBoolean(Bool b)
+{
+ Value v;
+ v.tag = _Boolean_Type;
+ v.int_32 = (bool)b;
+ return v;
+}
+
+inline Value Value::fromDouble(double d)
+{
+ Value v;
+ v.dbl = d;
+ return v;
+}
+
+inline Value Value::fromInt32(int i)
+{
+ Value v;
+ v.tag = _Integer_Type;
+ v.int_32 = i;
+ return v;
+}
+
+inline Value Value::fromUInt32(uint i)
+{
+ Value v;
+ if (i < INT_MAX) {
+ v.tag = _Integer_Type;
+ v.int_32 = (int)i;
+ } else {
+ v.dbl = i;
+ }
+ return v;
+}
+
+inline Value Value::fromString(String *s)
+{
+ Value v;
+#if QT_POINTER_SIZE == 8
+ v.val = (quint64)s;
+ v.val |= quint64(_String_Type) << Tag_Shift;
+#else
+ v.tag = _String_Type;
+ v.s = s;
+#endif
+ return v;
+}
+
+inline Value Value::fromObject(Object *o)
+{
+ Value v;
+#if QT_POINTER_SIZE == 8
+ v.val = (quint64)o;
+ v.val |= quint64(_Object_Type) << Tag_Shift;
+#else
+ v.tag = _Object_Type;
+ v.o = o;
+#endif
+ return v;
+}
+
+inline bool Value::toBoolean() const
+{
+ switch (type()) {
+ case Value::Undefined_Type:
+ case Value::Null_Type:
+ return false;
+ case Value::Boolean_Type:
+ case Value::Integer_Type:
+ return (bool)int_32;
+ case Value::String_Type:
+ return stringValue()->toQString().length() > 0;
+ case Value::Object_Type:
+ return true;
+ default: // double
+ if (! doubleValue() || std::isnan(doubleValue()))
+ return false;
+ return true;
+ }
+}
+
+inline Object *Value::toObject(ExecutionContext *ctx) const
+{
+ if (isObject())
+ return objectValue();
+ return __qmljs_convert_to_object(ctx, *this);
+}
+
+inline int Value::toInt32() const
+{
+ if (isConvertibleToInt())
+ return int_32;
+ double d;
+ if (isDouble())
+ d = dbl;
+ else
+ d = __qmljs_to_number(*this);
+
+ const double D32 = 4294967296.0;
+ const double D31 = D32 / 2.0;
+
+ if ((d >= -D31 && d < D31))
+ return static_cast<int>(d);
+
+ return Value::toInt32(__qmljs_to_number(*this));
+}
+
+inline unsigned int Value::toUInt32() const
+{
+ if (isConvertibleToInt())
+ return (unsigned) int_32;
+ double d;
+ if (isDouble())
+ d = dbl;
+ else
+ d = __qmljs_to_number(*this);
+
+ const double D32 = 4294967296.0;
+ if (dbl >= 0 && dbl < D32)
+ return static_cast<uint>(dbl);
+ return toUInt32(d);
+}
+
+inline uint Value::asArrayIndex() const
+{
+ if (isInteger() && int_32 >= 0)
+ return (uint)int_32;
+ if (!isDouble())
+ return UINT_MAX;
+ uint idx = (uint)dbl;
+ if (idx != dbl)
+ return UINT_MAX;
+ return idx;
+}
+
+inline uint Value::asArrayLength(bool *ok) const
+{
+ *ok = true;
+ if (isConvertibleToInt() && int_32 >= 0)
+ return (uint)int_32;
+ if (isDouble()) {
+ uint idx = (uint)dbl;
+ if ((double)idx != dbl) {
+ *ok = false;
+ return UINT_MAX;
+ }
+ return idx;
+ }
+ if (isString())
+ return stringValue()->toUInt(ok);
+
+ uint idx = toUInt32();
+ double d = toNumber();
+ if (d != idx) {
+ *ok = false;
+ return UINT_MAX;
+ }
+ return idx;
+}
+
+inline String *Value::asString() const
+{
+ if (isString())
+ return stringValue();
+ return 0;
+}
+
+inline Managed *Value::asManaged() const
+{
+ if (isObject() || isString())
+ return managed();
+ return 0;
+}
+
+inline Object *Value::asObject() const
+{
+ return isObject() ? objectValue() : 0;
+}
+
+inline FunctionObject *Value::asFunctionObject() const
+{
+ return isObject() ? managed()->asFunctionObject() : 0;
+}
+
+inline BooleanObject *Value::asBooleanObject() const
+{
+ return isObject() ? managed()->asBooleanObject() : 0;
+}
+
+inline NumberObject *Value::asNumberObject() const
+{
+ return isObject() ? managed()->asNumberObject() : 0;
+}
+
+inline StringObject *Value::asStringObject() const
+{
+ return isObject() ? managed()->asStringObject() : 0;
+}
+
+inline DateObject *Value::asDateObject() const
+{
+ return isObject() ? managed()->asDateObject() : 0;
+}
+
+inline ArrayObject *Value::asArrayObject() const
+{
+ return isObject() ? managed()->asArrayObject() : 0;
+}
+
+inline ErrorObject *Value::asErrorObject() const
+{
+ return isObject() ? managed()->asErrorObject() : 0;
+}
+
+// ###
+inline Value Managed::construct(Value *args, int argc) {
+ return vtbl->construct(this, args, argc);
+}
+inline Value Managed::call(const Value &thisObject, Value *args, int argc) {
+ return vtbl->call(this, thisObject, args, argc);
+}
+
+struct PersistentValuePrivate
+{
+ PersistentValuePrivate(const Value &v, ExecutionEngine *engine = 0, bool weak = false);
+ virtual ~PersistentValuePrivate();
+ Value value;
+ uint refcount;
+ QV4::ExecutionEngine *engine;
+ PersistentValuePrivate **prev;
+ PersistentValuePrivate *next;
+
+ void removeFromList();
+ void ref() { ++refcount; }
+ void deref();
+ PersistentValuePrivate *detach(const QV4::Value &value, bool weak = false);
+
+ bool checkEngine(QV4::ExecutionEngine *otherEngine) {
+ if (!engine) {
+ Q_ASSERT(!value.isObject());
+ engine = otherEngine;
+ }
+ return (engine == otherEngine);
+ }
+};
+
+class Q_QML_EXPORT PersistentValue
+{
+public:
+ PersistentValue() : d(0) {}
+ PersistentValue(const Value &val);
+ PersistentValue(const PersistentValue &other);
+ PersistentValue &operator=(const PersistentValue &other);
+ PersistentValue &operator=(const Value &other);
+ ~PersistentValue();
+
+ Value value() const {
+ return d ? d->value : Value::emptyValue();
+ }
+
+ ExecutionEngine *engine() {
+ if (!d)
+ return 0;
+ Managed *m = d->value.asManaged();
+ return m ? m->engine() : 0;
+ }
+
+ operator Value() const { return value(); }
+
+ bool isEmpty() const { return !d || d->value.isEmpty(); }
+ void clear() {
+ *this = PersistentValue();
+ }
+
+private:
+ PersistentValuePrivate *d;
+};
+
+class Q_QML_EXPORT WeakValue
+{
+public:
+ WeakValue() : d(0) {}
+ WeakValue(const Value &val);
+ WeakValue(const WeakValue &other);
+ WeakValue &operator=(const WeakValue &other);
+ WeakValue &operator=(const Value &other);
+ ~WeakValue();
+
+ Value value() const {
+ return d ? d->value : Value::emptyValue();
+ }
+
+ ExecutionEngine *engine() {
+ if (!d)
+ return 0;
+ Managed *m = d->value.asManaged();
+ return m ? m->engine() : 0;
+ }
+
+ operator Value() const { return value(); }
+
+ bool isEmpty() const { return !d || d->value.isEmpty(); }
+ void clear() {
+ *this = WeakValue();
+ }
+
+ void markOnce();
+
+private:
+ PersistentValuePrivate *d;
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4/qv4variantobject.cpp b/src/qml/qml/v4/qv4variantobject.cpp
new file mode 100644
index 0000000000..f18c5b582e
--- /dev/null
+++ b/src/qml/qml/v4/qv4variantobject.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4variantobject_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4objectproto_p.h"
+#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+DEFINE_MANAGED_VTABLE(VariantObject);
+
+VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value)
+ : Object(engine)
+ , ExecutionEngine::ScarceResourceData(value)
+ , m_vmePropertyReferenceCount(0)
+{
+ vtbl = &static_vtbl;
+ prototype = engine->variantPrototype;
+ if (isScarce())
+ internalClass->engine->scarceResources.insert(this);
+}
+
+QVariant VariantObject::toVariant(const QV4::Value &v)
+{
+ if (Object *o = v.asObject())
+ return o->engine()->v8Engine->variantFromJS(v);
+
+ if (v.isString())
+ return QVariant(v.stringValue()->toQString());
+ if (v.isBoolean())
+ return QVariant(v.booleanValue());
+ if (v.isNumber()) {
+ QV4::Value val = v;
+ if (val.isInt32())
+ return QVariant(val.integerValue());
+ return QVariant(v.asDouble());
+ }
+ if (v.isNull())
+ return QVariant(QMetaType::VoidStar, 0);
+ assert (v.isUndefined() || v.isEmpty());
+ return QVariant();
+}
+
+bool VariantObject::isScarce() const
+{
+ QVariant::Type t = data.type();
+ return t == QVariant::Pixmap || t == QVariant::Image;
+}
+
+void VariantObject::destroy(Managed *that)
+{
+ VariantObject *v = static_cast<VariantObject *>(that);
+ if (v->isScarce())
+ v->node.remove();
+ v->~VariantObject();
+}
+
+bool VariantObject::isEqualTo(Managed *m, Managed *other)
+{
+ QV4::VariantObject *lv = m->as<QV4::VariantObject>();
+ assert(lv);
+
+ if (QV4::VariantObject *rv = other->as<QV4::VariantObject>())
+ return lv->data == rv->data;
+
+ if (QV4::QmlValueTypeWrapper *v = other->as<QmlValueTypeWrapper>())
+ return v->isEqual(lv->data);
+
+ return false;
+}
+
+void VariantObject::addVmePropertyReference()
+{
+ if (isScarce() && ++m_vmePropertyReferenceCount == 1) {
+ // remove from the ep->scarceResources list
+ // since it is now no longer eligible to be
+ // released automatically by the engine.
+ node.remove();
+ }
+}
+
+void VariantObject::removeVmePropertyReference()
+{
+ if (isScarce() && --m_vmePropertyReferenceCount == 0) {
+ // and add to the ep->scarceResources list
+ // since it is now eligible to be released
+ // automatically by the engine.
+ internalClass->engine->scarceResources.insert(this);
+ }
+}
+
+
+VariantPrototype::VariantPrototype(ExecutionEngine *engine)
+ : VariantObject(engine, QVariant())
+{
+ prototype = engine->objectPrototype;
+}
+
+void VariantPrototype::init(ExecutionEngine *engine)
+{
+ defineDefaultProperty(engine, QStringLiteral("preserve"), method_preserve, 0);
+ defineDefaultProperty(engine, QStringLiteral("destroy"), method_destroy, 0);
+ defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0);
+ defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0);
+}
+
+QV4::Value VariantPrototype::method_preserve(SimpleCallContext *ctx)
+{
+ VariantObject *o = ctx->thisObject.as<QV4::VariantObject>();
+ if (o && o->isScarce())
+ o->node.remove();
+ return Value::undefinedValue();
+}
+
+QV4::Value VariantPrototype::method_destroy(SimpleCallContext *ctx)
+{
+ VariantObject *o = ctx->thisObject.as<QV4::VariantObject>();
+ if (o) {
+ if (o->isScarce())
+ o->node.remove();
+ o->data = QVariant();
+ }
+ return QV4::Value::undefinedValue();
+}
+
+QV4::Value VariantPrototype::method_toString(SimpleCallContext *ctx)
+{
+ VariantObject *o = ctx->thisObject.as<QV4::VariantObject>();
+ if (!o)
+ return Value::undefinedValue();
+ QString result = o->data.toString();
+ if (result.isEmpty() && !o->data.canConvert(QVariant::String))
+ result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->data.typeName()));
+ return Value::fromString(ctx->engine->newString(result));
+}
+
+QV4::Value VariantPrototype::method_valueOf(SimpleCallContext *ctx)
+{
+ VariantObject *o = ctx->thisObject.as<QV4::VariantObject>();
+ if (o) {
+ QVariant v = o->data;
+ switch (v.type()) {
+ case QVariant::Invalid:
+ return Value::undefinedValue();
+ case QVariant::String:
+ return Value::fromString(ctx->engine->newString(v.toString()));
+ case QVariant::Int:
+ return Value::fromInt32(v.toInt());
+ case QVariant::Double:
+ case QVariant::UInt:
+ return Value::fromDouble(v.toDouble());
+ case QVariant::Bool:
+ return Value::fromBoolean(v.toBool());
+ default:
+ break;
+ }
+ }
+ return ctx->thisObject;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4variantobject_p.h b/src/qml/qml/v4/qv4variantobject_p.h
new file mode 100644
index 0000000000..876539aae1
--- /dev/null
+++ b/src/qml/qml/v4/qv4variantobject_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4VARIANTOBJECT_P_H
+#define QV4VARIANTOBJECT_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/qglobal.h>
+#include <QtQml/qqmllist.h>
+#include <QtCore/qvariant.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4object_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct VariantObject : Object, public ExecutionEngine::ScarceResourceData
+{
+ Q_MANAGED
+public:
+ VariantObject(ExecutionEngine *engine, const QVariant &value);
+
+ static QVariant toVariant(const QV4::Value &v);
+
+ void addVmePropertyReference();
+ void removeVmePropertyReference();
+ bool isScarce() const;
+ int m_vmePropertyReferenceCount;
+
+ static void destroy(Managed *that);
+ static bool isEqualTo(Managed *m, Managed *other);
+};
+
+struct VariantPrototype : VariantObject
+{
+public:
+ VariantPrototype(ExecutionEngine *engine);
+
+ void init(ExecutionEngine *engine);
+
+ static Value method_preserve(SimpleCallContext *ctx);
+ static Value method_destroy(SimpleCallContext *ctx);
+ static Value method_toString(SimpleCallContext *ctx);
+ static Value method_valueOf(SimpleCallContext *ctx);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri
index b6784851d8..1c63b4962a 100644
--- a/src/qml/qml/v4/v4.pri
+++ b/src/qml/qml/v4/v4.pri
@@ -1,15 +1,198 @@
+include(llvm_installation.pri)
+include(../../../3rdparty/masm/masm-defs.pri)
+
+CONFIG += exceptions
+
+!llvm: DEFINES += QMLJS_NO_LLVM
+
+CONFIG += warn_off
+
+INCLUDEPATH += $$PWD
+INCLUDEPATH += $$OUT_PWD
+
+SOURCES += \
+ $$PWD/qv4codegen.cpp \
+ $$PWD/qv4ssa.cpp \
+ $$PWD/qv4jsir.cpp \
+ $$PWD/qv4engine.cpp \
+ $$PWD/qv4context.cpp \
+ $$PWD/qv4runtime.cpp \
+ $$PWD/qv4value.cpp \
+ $$PWD/qv4syntaxchecker.cpp \
+ $$PWD/qv4isel_masm.cpp \
+ $$PWD/llvm_runtime.cpp \
+ $$PWD/qv4isel_p.cpp \
+ $$PWD/qv4debugging.cpp \
+ $$PWD/qv4lookup.cpp \
+ $$PWD/qv4identifier.cpp \
+ $$PWD/qv4identifiertable.cpp \
+ $$PWD/qv4mm.cpp \
+ $$PWD/qv4managed.cpp \
+ $$PWD/qv4internalclass.cpp \
+ $$PWD/qv4sparsearray.cpp \
+ $$PWD/qv4arrayobject.cpp \
+ $$PWD/qv4argumentsobject.cpp \
+ $$PWD/qv4booleanobject.cpp \
+ $$PWD/qv4dateobject.cpp \
+ $$PWD/qv4errorobject.cpp \
+ $$PWD/qv4function.cpp \
+ $$PWD/qv4functionobject.cpp \
+ $$PWD/qv4globalobject.cpp \
+ $$PWD/qv4jsonobject.cpp \
+ $$PWD/qv4mathobject.cpp \
+ $$PWD/qv4numberobject.cpp \
+ $$PWD/qv4object.cpp \
+ $$PWD/qv4objectproto.cpp \
+ $$PWD/qv4regexpobject.cpp \
+ $$PWD/qv4stringobject.cpp \
+ $$PWD/qv4variantobject.cpp \
+ $$PWD/qv4string.cpp \
+ $$PWD/qv4objectiterator.cpp \
+ $$PWD/qv4regexp.cpp \
+ $$PWD/qv4unwindhelper.cpp \
+ $$PWD/qv4serialize.cpp \
+ $$PWD/qv4script.cpp \
+ $$PWD/qv4executableallocator.cpp \
+ $$PWD/qv4sequenceobject.cpp \
+ $$PWD/qv4include.cpp \
+ $$PWD/qv4qobjectwrapper.cpp \
+ $$PWD/qv4qmlextensions.cpp \
+ $$PWD/qv4stacktrace.cpp \
+ $$PWD/qv4exception.cpp
+
HEADERS += \
- $$PWD/qv4compiler_p.h \
- $$PWD/qv4compiler_p_p.h \
- $$PWD/qv4ir_p.h \
- $$PWD/qv4irbuilder_p.h \
- $$PWD/qv4instruction_p.h \
- $$PWD/qv4bindings_p.h \
- $$PWD/qv4program_p.h \
+ $$PWD/qv4global_p.h \
+ $$PWD/qv4codegen_p.h \
+ $$PWD/qv4ssa_p.h \
+ $$PWD/qv4jsir_p.h \
+ $$PWD/qv4engine_p.h \
+ $$PWD/qv4context_p.h \
+ $$PWD/qv4runtime_p.h \
+ $$PWD/qv4math_p.h \
+ $$PWD/qv4value_p.h \
+ $$PWD/qv4value_def_p.h \
+ $$PWD/qv4syntaxchecker_p.h \
+ $$PWD/qv4isel_masm_p.h \
+ $$PWD/qv4isel_p.h \
+ $$PWD/qv4isel_util_p.h \
+ $$PWD/qv4debugging_p.h \
+ $$PWD/qv4lookup_p.h \
+ $$PWD/qv4identifier_p.h \
+ $$PWD/qv4identifiertable_p.h \
+ $$PWD/qv4mm_p.h \
+ $$PWD/qv4managed_p.h \
+ $$PWD/qv4internalclass_p.h \
+ $$PWD/qv4sparsearray_p.h \
+ $$PWD/qv4arrayobject_p.h \
+ $$PWD/qv4argumentsobject_p.h \
+ $$PWD/qv4booleanobject_p.h \
+ $$PWD/qv4dateobject_p.h \
+ $$PWD/qv4errorobject_p.h \
+ $$PWD/qv4function_p.h \
+ $$PWD/qv4functionobject_p.h \
+ $$PWD/qv4globalobject_p.h \
+ $$PWD/qv4jsonobject_p.h \
+ $$PWD/qv4mathobject_p.h \
+ $$PWD/qv4numberobject_p.h \
+ $$PWD/qv4object_p.h \
+ $$PWD/qv4objectproto_p.h \
+ $$PWD/qv4regexpobject_p.h \
+ $$PWD/qv4stringobject_p.h \
+ $$PWD/qv4variantobject_p.h \
+ $$PWD/qv4string_p.h \
+ $$PWD/qv4property_p.h \
+ $$PWD/qv4objectiterator_p.h \
+ $$PWD/qv4regexp_p.h \
+ $$PWD/qv4unwindhelper_p.h \
+ $$PWD/qv4unwindhelper_p-dw2.h \
+ $$PWD/qv4unwindhelper_p-arm.h \
+ $$PWD/qv4serialize_p.h \
+ $$PWD/qv4script_p.h \
+ $$PWD/qv4util_p.h \
+ $$PWD/qv4executableallocator_p.h \
+ $$PWD/qv4sequenceobject_p.h \
+ $$PWD/qv4include_p.h \
+ $$PWD/qv4qobjectwrapper_p.h \
+ $$PWD/qv4qmlextensions_p.h \
+ $$PWD/qv4stacktrace_p.h \
+ $$PWD/qv4exception_p.h
+
+llvm-libs {
SOURCES += \
- $$PWD/qv4compiler.cpp \
- $$PWD/qv4ir.cpp \
- $$PWD/qv4irbuilder.cpp \
- $$PWD/qv4instruction.cpp \
- $$PWD/qv4bindings.cpp \
+ $$PWD/qv4isel_llvm.cpp
+
+HEADERS += \
+ $$PWD/qv4isel_llvm_p.h \
+ $$PWD/qv4_llvm_p.h
+
+LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc
+DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\""
+DEFINES += QMLJS_WITH_LLVM
+
+INCLUDEPATH += \
+ $$system($$LLVM_CONFIG --includedir)
+
+QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden
+QMAKE_CXXFLAGS -= -pedantic
+QMAKE_CXXFLAGS -= -Wcovered-switch-default
+
+LIBS += \
+ $$system($$LLVM_CONFIG --ldflags) \
+ $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native)
+
+QMAKE_EXTRA_TARGETS += gen_llvm_runtime
+
+GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags)
+GEN_LLVM_RUNTIME_FLAGS -= -pedantic
+
+gen_llvm_runtime.target = llvm_runtime
+gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I$$PWD -I$$PWD/../3rdparty/masm $$join(QT.core.includes, " -I", "-I") $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC
+}
+
+# Use SSE2 floating point math on 32 bit instead of the default
+# 387 to make test results pass on 32 and on 64 bit builds.
+linux-g++*:isEqual(QT_ARCH,i386) {
+ QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse
+ QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse
+}
+
+linux*|mac {
+ LIBS += -ldl
+}
+
+# Only on Android/ARM at the moment, because only there we have issues
+# replacing __gnu_Unwind_Find_exidx with our own implementation,
+# and thus require static libgcc linkage.
+android:equals(QT_ARCH, "arm"):*g++* {
+ static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a)
+ LIBS += $$static_libgcc
+ SOURCES += $$PWD/qv4exception_gcc.cpp
+ DEFINES += V4_CXX_ABI_EXCEPTION
+}
+
+debug-with-libunwind {
+ UW_INC=$$(LIBUNWIND_INCLUDES)
+ isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES")
+ INCLUDEPATH += $$UW_INC
+ UW_LIBS=$$(LIBUNWIND_LIBS)
+ isEmpty(UW_LIBS): error("Please set LIBUNWIND_LIBS")
+ LIBS += -L$$UW_LIBS
+ equals(QT_ARCH, arm): LIBS += -lunwind-arm
+ LIBS += -lunwind-dwarf-common -lunwind-dwarf-local -lunwind-elf32 -lunwind
+ DEFINES += WTF_USE_LIBUNWIND_DEBUG=1
+}
+
+valgrind {
+ DEFINES += V4_USE_VALGRIND
+}
+
+ios: DEFINES += ENABLE_ASSEMBLER_WX_EXCLUSIVE=1
+
+win32 {
+ LIBS_PRIVATE += -lDbgHelp
+}
+
+include(moth/moth.pri)
+include(../../../3rdparty/masm/masm.pri)
+include(../../../3rdparty/double-conversion/double-conversion.pri)