aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/v4vm/debugging.cpp308
-rw-r--r--src/qml/qml/v4vm/debugging.h157
-rw-r--r--src/qml/qml/v4vm/llvm_installation.pri23
-rw-r--r--src/qml/qml/v4vm/llvm_runtime.cpp513
-rw-r--r--src/qml/qml/v4vm/moth/moth.pri13
-rw-r--r--src/qml/qml/v4vm/moth/qv4instr_moth.cpp15
-rw-r--r--src/qml/qml/v4vm/moth/qv4instr_moth_p.h527
-rw-r--r--src/qml/qml/v4vm/moth/qv4isel_moth.cpp812
-rw-r--r--src/qml/qml/v4vm/moth/qv4isel_moth_p.h169
-rw-r--r--src/qml/qml/v4vm/moth/qv4vme_moth.cpp532
-rw-r--r--src/qml/qml/v4vm/moth/qv4vme_moth_p.h35
-rw-r--r--src/qml/qml/v4vm/qcalculatehash_p.h73
-rw-r--r--src/qml/qml/v4vm/qv4_llvm_p.h65
-rw-r--r--src/qml/qml/v4vm/qv4alloca_p.h54
-rw-r--r--src/qml/qml/v4vm/qv4argumentsobject.cpp176
-rw-r--r--src/qml/qml/v4vm/qv4argumentsobject.h99
-rw-r--r--src/qml/qml/v4vm/qv4arrayobject.cpp859
-rw-r--r--src/qml/qml/v4vm/qv4arrayobject.h103
-rw-r--r--src/qml/qml/v4vm/qv4booleanobject.cpp97
-rw-r--r--src/qml/qml/v4vm/qv4booleanobject.h79
-rw-r--r--src/qml/qml/v4vm/qv4codegen.cpp3256
-rw-r--r--src/qml/qml/v4vm/qv4codegen_p.h444
-rw-r--r--src/qml/qml/v4vm/qv4context.cpp576
-rw-r--r--src/qml/qml/v4vm/qv4context.h194
-rw-r--r--src/qml/qml/v4vm/qv4dateobject.cpp1316
-rw-r--r--src/qml/qml/v4vm/qv4dateobject.h132
-rw-r--r--src/qml/qml/v4vm/qv4engine.cpp551
-rw-r--r--src/qml/qml/v4vm/qv4engine.h271
-rw-r--r--src/qml/qml/v4vm/qv4errorobject.cpp238
-rw-r--r--src/qml/qml/v4vm/qv4errorobject.h235
-rw-r--r--src/qml/qml/v4vm/qv4executableallocator.cpp208
-rw-r--r--src/qml/qml/v4vm/qv4executableallocator.h121
-rw-r--r--src/qml/qml/v4vm/qv4functionobject.cpp522
-rw-r--r--src/qml/qml/v4vm/qv4functionobject.h243
-rw-r--r--src/qml/qml/v4vm/qv4global.h169
-rw-r--r--src/qml/qml/v4vm/qv4globalobject.cpp731
-rw-r--r--src/qml/qml/v4vm/qv4globalobject.h93
-rw-r--r--src/qml/qml/v4vm/qv4identifier.h111
-rw-r--r--src/qml/qml/v4vm/qv4internalclass.cpp188
-rw-r--r--src/qml/qml/v4vm/qv4internalclass.h91
-rw-r--r--src/qml/qml/v4vm/qv4isel_llvm.cpp1375
-rw-r--r--src/qml/qml/v4vm/qv4isel_llvm_p.h177
-rw-r--r--src/qml/qml/v4vm/qv4isel_masm.cpp1252
-rw-r--r--src/qml/qml/v4vm/qv4isel_masm_p.h894
-rw-r--r--src/qml/qml/v4vm/qv4isel_p.cpp398
-rw-r--r--src/qml/qml/v4vm/qv4isel_p.h158
-rw-r--r--src/qml/qml/v4vm/qv4isel_util_p.h77
-rw-r--r--src/qml/qml/v4vm/qv4jsir.cpp948
-rw-r--r--src/qml/qml/v4vm/qv4jsir_p.h821
-rw-r--r--src/qml/qml/v4vm/qv4jsonobject.cpp936
-rw-r--r--src/qml/qml/v4vm/qv4jsonobject.h65
-rw-r--r--src/qml/qml/v4vm/qv4lookup.cpp332
-rw-r--r--src/qml/qml/v4vm/qv4lookup.h144
-rw-r--r--src/qml/qml/v4vm/qv4managed.cpp187
-rw-r--r--src/qml/qml/v4vm/qv4managed.h247
-rw-r--r--src/qml/qml/v4vm/qv4math.h120
-rw-r--r--src/qml/qml/v4vm/qv4mathobject.cpp311
-rw-r--r--src/qml/qml/v4vm/qv4mathobject.h80
-rw-r--r--src/qml/qml/v4vm/qv4mm.cpp493
-rw-r--r--src/qml/qml/v4vm/qv4mm.h155
-rw-r--r--src/qml/qml/v4vm/qv4numberobject.cpp237
-rw-r--r--src/qml/qml/v4vm/qv4numberobject.h83
-rw-r--r--src/qml/qml/v4vm/qv4object.cpp1177
-rw-r--r--src/qml/qml/v4vm/qv4object.h417
-rw-r--r--src/qml/qml/v4vm/qv4objectiterator.cpp185
-rw-r--r--src/qml/qml/v4vm/qv4objectiterator.h84
-rw-r--r--src/qml/qml/v4vm/qv4objectproto.cpp565
-rw-r--r--src/qml/qml/v4vm/qv4objectproto.h104
-rw-r--r--src/qml/qml/v4vm/qv4property.h152
-rw-r--r--src/qml/qml/v4vm/qv4regexp.cpp167
-rw-r--r--src/qml/qml/v4vm/qv4regexp.h149
-rw-r--r--src/qml/qml/v4vm/qv4regexpobject.cpp256
-rw-r--r--src/qml/qml/v4vm/qv4regexpobject.h108
-rw-r--r--src/qml/qml/v4vm/qv4runtime.cpp1319
-rw-r--r--src/qml/qml/v4vm/qv4runtime.h745
-rw-r--r--src/qml/qml/v4vm/qv4sparsearray.cpp464
-rw-r--r--src/qml/qml/v4vm/qv4sparsearray.h369
-rw-r--r--src/qml/qml/v4vm/qv4string.cpp242
-rw-r--r--src/qml/qml/v4vm/qv4string.h136
-rw-r--r--src/qml/qml/v4vm/qv4stringobject.cpp726
-rw-r--r--src/qml/qml/v4vm/qv4stringobject.h108
-rw-r--r--src/qml/qml/v4vm/qv4syntaxchecker.cpp119
-rw-r--r--src/qml/qml/v4vm/qv4syntaxchecker_p.h73
-rw-r--r--src/qml/qml/v4vm/qv4unwindhelper.cpp37
-rw-r--r--src/qml/qml/v4vm/qv4unwindhelper.h27
-rw-r--r--src/qml/qml/v4vm/qv4unwindhelper_p-arm.h176
-rw-r--r--src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h189
-rw-r--r--src/qml/qml/v4vm/qv4util.h74
-rw-r--r--src/qml/qml/v4vm/qv4v8.cpp2141
-rw-r--r--src/qml/qml/v4vm/qv4v8.h2581
-rw-r--r--src/qml/qml/v4vm/qv4value.cpp214
-rw-r--r--src/qml/qml/v4vm/qv4value.h572
-rw-r--r--src/qml/qml/v4vm/v4.pri8
-rw-r--r--src/qml/qml/v4vm/v4.pro177
94 files changed, 37750 insertions, 0 deletions
diff --git a/src/qml/qml/v4vm/debugging.cpp b/src/qml/qml/v4vm/debugging.cpp
new file mode 100644
index 0000000000..6137e64f89
--- /dev/null
+++ b/src/qml/qml/v4vm/debugging.cpp
@@ -0,0 +1,308 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "debugging.h"
+#include "qv4object.h"
+#include "qv4functionobject.h"
+#include <iostream>
+
+#define LOW_LEVEL_DEBUGGING_HELPERS
+
+using namespace QQmlJS;
+using namespace QQmlJS::Debugging;
+
+FunctionState::FunctionState(VM::ExecutionContext *context)
+ : _context(context)
+{
+ if (debugger())
+ debugger()->enterFunction(this);
+}
+
+FunctionState::~FunctionState()
+{
+ if (debugger())
+ debugger()->leaveFunction(this);
+}
+
+VM::Value *FunctionState::argument(unsigned idx)
+{
+ VM::CallContext *c = _context->asCallContext();
+ if (!c || idx >= c->argumentCount)
+ return 0;
+ return c->arguments + idx;
+}
+
+VM::Value *FunctionState::local(unsigned idx)
+{
+ VM::CallContext *c = _context->asCallContext();
+ if (c && idx < c->variableCount())
+ return c->locals + idx;
+ return 0;
+}
+
+#ifdef LOW_LEVEL_DEBUGGING_HELPERS
+Debugger *globalInstance = 0;
+
+void printStackTrace()
+{
+ if (globalInstance)
+ globalInstance->printStackTrace();
+ else
+ std::cerr << "No debugger." << std::endl;
+}
+#endif // DO_TRACE_INSTR
+
+Debugger::Debugger(VM::ExecutionEngine *engine)
+ : _engine(engine)
+{
+#ifdef LOW_LEVEL_DEBUGGING_HELPERS
+ globalInstance = this;
+#endif // DO_TRACE_INSTR
+}
+
+Debugger::~Debugger()
+{
+#ifdef LOW_LEVEL_DEBUGGING_HELPERS
+ globalInstance = 0;
+#endif // DO_TRACE_INSTR
+
+ qDeleteAll(_functionInfo.values());
+}
+
+void Debugger::addFunction(V4IR::Function *function)
+{
+ _functionInfo.insert(function, new FunctionDebugInfo(function));
+}
+
+void Debugger::setSourceLocation(V4IR::Function *function, unsigned line, unsigned column)
+{
+ _functionInfo[function]->setSourceLocation(line, column);
+}
+
+void Debugger::mapFunction(VM::Function *vmf, V4IR::Function *irf)
+{
+ _vmToIr.insert(vmf, irf);
+}
+
+FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const
+{
+ if (!function)
+ return 0;
+
+ if (function->function)
+ return _functionInfo[irFunction(function->function)];
+ else
+ return 0;
+}
+
+QString Debugger::name(VM::FunctionObject *function) const
+{
+ if (FunctionDebugInfo *i = debugInfo(function))
+ return i->name;
+
+ return QString();
+}
+
+void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context)
+{
+ _callStack.append(CallInfo(context, function));
+}
+
+void Debugger::justLeft(VM::ExecutionContext *context)
+{
+ int idx = callIndex(context);
+ if (idx < 0)
+ qDebug() << "Oops, leaving a function that was not registered...?";
+ else
+ _callStack.resize(idx);
+}
+
+void Debugger::enterFunction(FunctionState *state)
+{
+ _callStack[callIndex(state->context())].state = state;
+
+#ifdef DO_TRACE_INSTR
+ QString n = name(_callStack[callIndex(state->context())].function);
+ std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl;
+// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i)
+// std::cerr << " " << i << ": " << currentArg(i) << std::endl;
+#endif // DO_TRACE_INSTR
+}
+
+void Debugger::leaveFunction(FunctionState *state)
+{
+ _callStack[callIndex(state->context())].state = 0;
+}
+
+void Debugger::aboutToThrow(const VM::Value &value)
+{
+ qDebug() << "*** We are about to throw...:" << value.toString(currentState()->context())->toQString();
+}
+
+FunctionState *Debugger::currentState() const
+{
+ if (_callStack.isEmpty())
+ return 0;
+ else
+ return _callStack.last().state;
+}
+
+const char *Debugger::currentArg(unsigned idx) const
+{
+ FunctionState *state = currentState();
+ return qPrintable(state->argument(idx)->toString(state->context())->toQString());
+}
+
+const char *Debugger::currentLocal(unsigned idx) const
+{
+ FunctionState *state = currentState();
+ return qPrintable(state->local(idx)->toString(state->context())->toQString());
+}
+
+const char *Debugger::currentTemp(unsigned idx) const
+{
+ FunctionState *state = currentState();
+ return qPrintable(state->temp(idx)->toString(state->context())->toQString());
+}
+
+void Debugger::printStackTrace() const
+{
+ for (int i = _callStack.size() - 1; i >=0; --i) {
+ QString n = name(_callStack[i].function);
+ std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl;
+ }
+}
+
+int Debugger::callIndex(VM::ExecutionContext *context)
+{
+ for (int idx = _callStack.size() - 1; idx >= 0; --idx) {
+ if (_callStack[idx].context == context)
+ return idx;
+ }
+
+ return -1;
+}
+
+V4IR::Function *Debugger::irFunction(VM::Function *vmf) const
+{
+ return _vmToIr[vmf];
+}
+
+static void realDumpValue(VM::Value v, VM::ExecutionContext *ctx, std::string prefix)
+{
+ using namespace VM;
+ 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 VM::Managed::Type_Invalid: cout << "Invalid"; break;
+ case VM::Managed::Type_String: cout << "String"; break;
+ case VM::Managed::Type_Object: cout << "Object"; break;
+ case VM::Managed::Type_ArrayObject: cout << "ArrayObject"; break;
+ case VM::Managed::Type_FunctionObject: cout << "FunctionObject"; break;
+ case VM::Managed::Type_BooleanObject: cout << "BooleanObject"; break;
+ case VM::Managed::Type_NumberObject: cout << "NumberObject"; break;
+ case VM::Managed::Type_StringObject: cout << "StringObject"; break;
+ case VM::Managed::Type_DateObject: cout << "DateObject"; break;
+ case VM::Managed::Type_RegExpObject: cout << "RegExpObject"; break;
+ case VM::Managed::Type_ErrorObject: cout << "ErrorObject"; break;
+ case VM::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break;
+ case VM::Managed::Type_JSONObject: cout << "JSONObject"; break;
+ case VM::Managed::Type_MathObject: cout << "MathObject"; break;
+ case VM::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(ctx, d, attrs);
+ cout << prefix << "\tvalue:" << endl;
+ realDumpValue(pval, ctx, prefix + "\t");
+ }
+}
+
+void dumpValue(VM::Value v, VM::ExecutionContext *ctx)
+{
+ realDumpValue(v, ctx, std::string(""));
+}
diff --git a/src/qml/qml/v4vm/debugging.h b/src/qml/qml/v4vm/debugging.h
new file mode 100644
index 0000000000..b337f41c51
--- /dev/null
+++ b/src/qml/qml/v4vm/debugging.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4engine.h"
+#include "qv4context.h"
+
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+namespace V4IR {
+struct BasicBlock;
+struct Function;
+} // namespace IR
+
+namespace Debugging {
+
+class Debugger;
+
+struct FunctionDebugInfo { // TODO: use opaque d-pointers here
+ QString name;
+ unsigned startLine, startColumn;
+
+ FunctionDebugInfo(V4IR::Function *function):
+ startLine(0), startColumn(0)
+ {
+ if (function->name)
+ name = *function->name;
+ }
+
+ void setSourceLocation(unsigned line, unsigned column)
+ { startLine = line; startColumn = column; }
+};
+
+class FunctionState
+{
+public:
+ FunctionState(VM::ExecutionContext *context);
+ virtual ~FunctionState();
+
+ virtual VM::Value *argument(unsigned idx);
+ virtual VM::Value *local(unsigned idx);
+ virtual VM::Value *temp(unsigned idx) = 0;
+
+ VM::ExecutionContext *context() const
+ { return _context; }
+
+ Debugger *debugger() const
+ { return _context->engine->debugger; }
+
+private:
+ VM::ExecutionContext *_context;
+};
+
+struct CallInfo
+{
+ VM::ExecutionContext *context;
+ VM::FunctionObject *function;
+ FunctionState *state;
+
+ CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0)
+ : context(context)
+ , function(function)
+ , state(state)
+ {}
+};
+
+class Q_V4_EXPORT Debugger
+{
+public:
+ Debugger(VM::ExecutionEngine *_engine);
+ ~Debugger();
+
+public: // compile-time interface
+ void addFunction(V4IR::Function *function);
+ void setSourceLocation(V4IR::Function *function, unsigned line, unsigned column);
+ void mapFunction(VM::Function *vmf, V4IR::Function *irf);
+
+public: // run-time querying interface
+ FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const;
+ QString name(VM::FunctionObject *function) const;
+
+public: // execution hooks
+ void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context);
+ void justLeft(VM::ExecutionContext *context);
+ void enterFunction(FunctionState *state);
+ void leaveFunction(FunctionState *state);
+ void aboutToThrow(const VM::Value &value);
+
+public: // debugging hooks
+ FunctionState *currentState() const;
+ const char *currentArg(unsigned idx) const;
+ const char *currentLocal(unsigned idx) const;
+ const char *currentTemp(unsigned idx) const;
+ void printStackTrace() const;
+
+private:
+ int callIndex(VM::ExecutionContext *context);
+ V4IR::Function *irFunction(VM::Function *vmf) const;
+
+private: // TODO: use opaque d-pointers here
+ VM::ExecutionEngine *_engine;
+ QHash<V4IR::Function *, FunctionDebugInfo *> _functionInfo;
+ QHash<VM::Function *, V4IR::Function *> _vmToIr;
+ QVector<CallInfo> _callStack;
+};
+
+} // namespace Debugging
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // DEBUGGING_H
diff --git a/src/qml/qml/v4vm/llvm_installation.pri b/src/qml/qml/v4vm/llvm_installation.pri
new file mode 100644
index 0000000000..99e955fd2b
--- /dev/null
+++ b/src/qml/qml/v4vm/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/v4vm/llvm_runtime.cpp b/src/qml/qml/v4vm/llvm_runtime.cpp
new file mode 100644
index 0000000000..629ee4028c
--- /dev/null
+++ b/src/qml/qml/v4vm/llvm_runtime.cpp
@@ -0,0 +1,513 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4context.h"
+#include "qv4engine.h"
+#include <stdio.h>
+#include <setjmp.h>
+
+using namespace QQmlJS::VM;
+
+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/v4vm/moth/moth.pri b/src/qml/qml/v4vm/moth/moth.pri
new file mode 100644
index 0000000000..73bd893286
--- /dev/null
+++ b/src/qml/qml/v4vm/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/v4vm/moth/qv4instr_moth.cpp b/src/qml/qml/v4vm/moth/qv4instr_moth.cpp
new file mode 100644
index 0000000000..a2bad39e00
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4instr_moth.cpp
@@ -0,0 +1,15 @@
+#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/v4vm/moth/qv4instr_moth_p.h b/src/qml/qml/v4vm/moth/qv4instr_moth_p.h
new file mode 100644
index 0000000000..b29fb13a74
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4instr_moth_p.h
@@ -0,0 +1,527 @@
+#ifndef QV4INSTR_MOTH_P_H
+#define QV4INSTR_MOTH_P_H
+
+#include <QtCore/qglobal.h>
+#include "qv4object.h"
+
+#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(CreateValue, createValue) \
+ F(CreateProperty, createProperty) \
+ F(CreateActivationProperty, createActivationProperty) \
+ F(Jump, jump) \
+ F(CJump, cjump) \
+ F(Unop, unop) \
+ F(Binop, binop) \
+ 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;
+#else
+# define MOTH_INSTR_HEADER quint8 instructionType;
+#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
+ };
+ VM::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 VM::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;
+ }
+ };
+
+ 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
+ VM::Function *value;
+ Param result;
+ };
+ struct instr_loadName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param result;
+ };
+ struct instr_storeName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param source;
+ };
+ struct instr_loadProperty {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param base;
+ Param result;
+ };
+ struct instr_storeProperty {
+ MOTH_INSTR_HEADER
+ VM::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;
+ VM::String *exceptionVarName;
+ Param exceptionVar;
+ };
+ struct instr_callValue {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param dest;
+ Param result;
+ };
+ struct instr_callProperty {
+ MOTH_INSTR_HEADER
+ VM::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
+ VM::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
+ VM::String *member;
+ Param base;
+ Param result;
+ };
+ struct instr_callBuiltinDeleteSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinDeleteName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofMember {
+ MOTH_INSTR_HEADER
+ VM::String *member;
+ Param base;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinTypeofValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncMember {
+ MOTH_INSTR_HEADER
+ Param base;
+ VM::String *member;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinPostIncValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecMember {
+ MOTH_INSTR_HEADER
+ Param base;
+ VM::String *member;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecSubscript {
+ MOTH_INSTR_HEADER
+ Param base;
+ Param index;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecName {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param result;
+ };
+ struct instr_callBuiltinPostDecValue {
+ MOTH_INSTR_HEADER
+ Param value;
+ Param result;
+ };
+ struct instr_callBuiltinDeclareVar {
+ MOTH_INSTR_HEADER
+ VM::String *varName;
+ bool isDeletable;
+ };
+ struct instr_callBuiltinDefineGetterSetter {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param object;
+ Param getter;
+ Param setter;
+ };
+ struct instr_callBuiltinDefineProperty {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ Param object;
+ Param value;
+ };
+ struct instr_callBuiltinDefineArray {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param result;
+ };
+ struct instr_createValue {
+ MOTH_INSTR_HEADER
+ quint32 argc;
+ quint32 args;
+ Param func;
+ Param result;
+ };
+ struct instr_createProperty {
+ MOTH_INSTR_HEADER
+ VM::String *name;
+ quint32 argc;
+ quint32 args;
+ Param base;
+ Param result;
+ };
+ struct instr_createActivationProperty {
+ MOTH_INSTR_HEADER
+ VM::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
+ VM::UnaryOpName alu;
+ Param source;
+ Param result;
+ };
+ struct instr_binop {
+ MOTH_INSTR_HEADER
+ VM::BinOp alu;
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_loadThis {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ struct instr_inplaceElementOp {
+ MOTH_INSTR_HEADER
+ VM::InplaceBinOpElement alu;
+ Param base;
+ Param index;
+ Param source;
+ };
+ struct instr_inplaceMemberOp {
+ MOTH_INSTR_HEADER
+ VM::InplaceBinOpMember alu;
+ VM::String *member;
+ Param base;
+ Param source;
+ };
+ struct instr_inplaceNameOp {
+ MOTH_INSTR_HEADER
+ VM::InplaceBinOpName alu;
+ VM::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_createValue createValue;
+ instr_createProperty createProperty;
+ instr_createActivationProperty createActivationProperty;
+ instr_jump jump;
+ instr_cjump cjump;
+ instr_unop unop;
+ instr_binop binop;
+ 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
+
+#endif // QV4INSTR_MOTH_P_H
diff --git a/src/qml/qml/v4vm/moth/qv4isel_moth.cpp b/src/qml/qml/v4vm/moth/qv4isel_moth.cpp
new file mode 100644
index 0000000000..00ae4e1029
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4isel_moth.cpp
@@ -0,0 +1,812 @@
+#include "qv4isel_util_p.h"
+#include "qv4isel_moth_p.h"
+#include "qv4vme_moth_p.h"
+#include "qv4functionobject.h"
+#include "qv4regexpobject.h"
+#include "debugging.h"
+
+using namespace QQmlJS;
+using namespace QQmlJS::Moth;
+
+namespace {
+
+inline VM::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 VM::__qmljs_bit_and;
+ case V4IR::OpBitOr:
+ return VM::__qmljs_bit_or;
+ case V4IR::OpBitXor:
+ return VM::__qmljs_bit_xor;
+ case V4IR::OpAdd:
+ return VM::__qmljs_add;
+ case V4IR::OpSub:
+ return VM::__qmljs_sub;
+ case V4IR::OpMul:
+ return VM::__qmljs_mul;
+ case V4IR::OpDiv:
+ return VM::__qmljs_div;
+ case V4IR::OpMod:
+ return VM::__qmljs_mod;
+ case V4IR::OpLShift:
+ return VM::__qmljs_shl;
+ case V4IR::OpRShift:
+ return VM::__qmljs_shr;
+ case V4IR::OpURShift:
+ return VM::__qmljs_ushr;
+ case V4IR::OpGt:
+ return VM::__qmljs_gt;
+ case V4IR::OpLt:
+ return VM::__qmljs_lt;
+ case V4IR::OpGe:
+ return VM::__qmljs_ge;
+ case V4IR::OpLe:
+ return VM::__qmljs_le;
+ case V4IR::OpEqual:
+ return VM::__qmljs_eq;
+ case V4IR::OpNotEqual:
+ return VM::__qmljs_ne;
+ case V4IR::OpStrictEqual:
+ return VM::__qmljs_se;
+ case V4IR::OpStrictNotEqual:
+ return VM::__qmljs_sne;
+ case V4IR::OpInstanceof:
+ return VM::__qmljs_instanceof;
+ case V4IR::OpIn:
+ return VM::__qmljs_in;
+ case V4IR::OpAnd:
+ return 0;
+ case V4IR::OpOr:
+ return 0;
+ default:
+ assert(!"Unknown AluOp");
+ return 0;
+ }
+};
+
+} // anonymous namespace
+
+InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module)
+ : EvalInstructionSelection(engine, module)
+ , _function(0)
+ , _vmFunction(0)
+ , _block(0)
+ , _codeStart(0)
+ , _codeNext(0)
+ , _codeEnd(0)
+{
+}
+
+InstructionSelection::~InstructionSelection()
+{
+}
+
+void InstructionSelection::run(VM::Function *vmFunction, V4IR::Function *function)
+{
+ V4IR::BasicBlock *block;
+
+ QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > patches;
+ QHash<V4IR::BasicBlock *, ptrdiff_t> addrs;
+
+ int codeSize = 4096;
+ uchar *codeStart = new uchar[codeSize];
+ uchar *codeNext = codeStart;
+ uchar *codeEnd = codeStart + codeSize;
+
+ qSwap(_function, function);
+ qSwap(_vmFunction, vmFunction);
+ qSwap(block, _block);
+ qSwap(patches, _patches);
+ qSwap(addrs, _addrs);
+ qSwap(codeStart, _codeStart);
+ qSwap(codeNext, _codeNext);
+ qSwap(codeEnd, _codeEnd);
+
+ int locals = frameSize();
+ assert(locals >= 0);
+
+ Instruction::Push push;
+ push.value = quint32(locals);
+ addInstruction(push);
+
+ foreach (_block, _function->basicBlocks) {
+ _addrs.insert(_block, _codeNext - _codeStart);
+
+ foreach (V4IR::Stmt *s, _block->statements)
+ s->accept(this);
+ }
+
+ patchJumpAddresses();
+
+ _vmFunction->code = VME::exec;
+ _vmFunction->codeData = squeezeCode();
+
+ qSwap(_function, function);
+ qSwap(_vmFunction, vmFunction);
+ qSwap(block, _block);
+ 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::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(VM::Value::fromString(identifier(str)));
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp)
+{
+ VM::Value v = VM::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)
+{
+ VM::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)
+{
+ Instruction::MoveTemp move;
+ move.source = getParam(sourceTemp);
+ move.result = getResultParam(targetTemp);
+ addInstruction(move);
+}
+
+void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
+{
+ VM::UnaryOpName op = 0;
+ switch (oper) {
+ case V4IR::OpIfTrue: assert(!"unreachable"); break;
+ case V4IR::OpNot: op = VM::__qmljs_not; break;
+ case V4IR::OpUMinus: op = VM::__qmljs_uminus; break;
+ case V4IR::OpUPlus: op = VM::__qmljs_uplus; break;
+ case V4IR::OpCompl: op = VM::__qmljs_compl; break;
+ case V4IR::OpIncrement: op = VM::__qmljs_increment; break;
+ case V4IR::OpDecrement: op = VM::__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::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target)
+{
+ 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)
+{
+ VM::InplaceBinOpName op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break;
+ case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break;
+ case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break;
+ case V4IR::OpAdd: op = VM::__qmljs_inplace_add_name; break;
+ case V4IR::OpSub: op = VM::__qmljs_inplace_sub_name; break;
+ case V4IR::OpMul: op = VM::__qmljs_inplace_mul_name; break;
+ case V4IR::OpDiv: op = VM::__qmljs_inplace_div_name; break;
+ case V4IR::OpMod: op = VM::__qmljs_inplace_mod_name; break;
+ case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break;
+ case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break;
+ case V4IR::OpURShift: op = VM::__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)
+{
+ VM::InplaceBinOpElement op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break;
+ case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break;
+ case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break;
+ case V4IR::OpAdd: op = VM::__qmljs_inplace_add_element; break;
+ case V4IR::OpSub: op = VM::__qmljs_inplace_sub_element; break;
+ case V4IR::OpMul: op = VM::__qmljs_inplace_mul_element; break;
+ case V4IR::OpDiv: op = VM::__qmljs_inplace_div_element; break;
+ case V4IR::OpMod: op = VM::__qmljs_inplace_mod_element; break;
+ case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break;
+ case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break;
+ case V4IR::OpURShift: op = VM::__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)
+{
+ VM::InplaceBinOpMember op = 0;
+ switch (oper) {
+ case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break;
+ case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break;
+ case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break;
+ case V4IR::OpAdd: op = VM::__qmljs_inplace_add_member; break;
+ case V4IR::OpSub: op = VM::__qmljs_inplace_sub_member; break;
+ case V4IR::OpMul: op = VM::__qmljs_inplace_mul_member; break;
+ case V4IR::OpDiv: op = VM::__qmljs_inplace_div_member; break;
+ case V4IR::OpMod: op = VM::__qmljs_inplace_mod_member; break;
+ case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break;
+ case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break;
+ case V4IR::OpURShift: op = VM::__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()) {
+ // ok, only 1 argument in the call...
+ const int idx = e->expr->asTemp()->index;
+ // We can only pass a reference into the stack, which holds temps that
+ // are not arguments (idx >= 0) nor locals (idx >= localCound).
+ singleArgIsTemp = idx >= _function->locals.size() && e->expr->asTemp()->scope == 0;
+ }
+
+ 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 = e->expr->asTemp()->index - _function->locals.size();
+ } 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)
+{
+ 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 (_block->index + 1 != s->iffalse->index) {
+ 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(VM::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);
+}
+
+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
+
+ 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;
+}
+
+VM::String *InstructionSelection::identifier(const QString &s)
+{
+ VM::String *str = engine()->newIdentifier(s);
+ _vmFunction->identifiers.append(str);
+ return str;
+}
diff --git a/src/qml/qml/v4vm/moth/qv4isel_moth_p.h b/src/qml/qml/v4vm/moth/qv4isel_moth_p.h
new file mode 100644
index 0000000000..5e057139e2
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4isel_moth_p.h
@@ -0,0 +1,169 @@
+#ifndef QV4ISEL_MOTH_P_H
+#define QV4ISEL_MOTH_P_H
+
+#include "qv4global.h"
+#include "qv4isel_p.h"
+#include "qv4jsir_p.h"
+#include "qv4object.h"
+#include "qv4instr_moth_p.h"
+
+namespace QQmlJS {
+namespace Moth {
+
+class Q_V4_EXPORT InstructionSelection:
+ public V4IR::InstructionSelection,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(VM::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 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 *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::Temp *leftSource, V4IR::Temp *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)
+ {
+ typedef Instr::Param Param;
+ assert(e);
+
+ if (V4IR::Const *c = e->asConst()) {
+ return Param::createValue(convertToValue(c));
+ } else if (V4IR::Temp *t = e->asTemp()) {
+ const int index = t->index;
+ if (index < 0) {
+ return Param::createArgument(-index - 1, t->scope);
+ } else if (!t->scope) {
+ const int localCount = _function->locals.size();
+ if (index < localCount)
+ return Param::createLocal(index);
+ else
+ return Param::createTemp(index - localCount);
+ } else {
+ return Param::createScopedLocal(t->index, t->scope);
+ }
+ } else {
+ Q_UNIMPLEMENTED();
+ return Param();
+ }
+ }
+
+ 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 - _function->locals.size(); }
+ 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;
+
+ VM::String *identifier(const QString &s);
+
+ V4IR::Function *_function;
+ VM::Function *_vmFunction;
+ V4IR::BasicBlock *_block;
+
+ QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
+ QHash<V4IR::BasicBlock *, ptrdiff_t> _addrs;
+
+ uchar *_codeStart;
+ uchar *_codeNext;
+ uchar *_codeEnd;
+};
+
+class Q_V4_EXPORT ISelFactory: public EvalISelFactory
+{
+public:
+ virtual ~ISelFactory() {}
+ virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module)
+ { return new InstructionSelection(engine, module); }
+};
+
+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
+
+#endif // QV4ISEL_MOTH_P_H
diff --git a/src/qml/qml/v4vm/moth/qv4vme_moth.cpp b/src/qml/qml/v4vm/moth/qv4vme_moth.cpp
new file mode 100644
index 0000000000..a3c34bf340
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4vme_moth.cpp
@@ -0,0 +1,532 @@
+#include "qv4vme_moth_p.h"
+#include "qv4instr_moth_p.h"
+#include "qv4value.h"
+#include "debugging.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;
+
+class FunctionState: public Debugging::FunctionState
+{
+public:
+ FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code)
+ : Debugging::FunctionState(context)
+ , stack(0)
+ , stackSize(0)
+ , code(code)
+ {}
+
+ virtual VM::Value *temp(unsigned idx) { return stack + idx; }
+
+ void setStack(VM::Value *stack, unsigned stackSize)
+ { this->stack = stack; this->stackSize = stackSize; }
+
+private:
+ VM::Value *stack;
+ unsigned stackSize;
+ const uchar **code;
+};
+
+#define MOTH_BEGIN_INSTR_COMMON(I) { \
+ const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \
+ code += InstrMeta<(int)Instr::I>::Size; \
+ Q_UNUSED(instr); \
+ TRACE_INSTR(I)
+
+#ifdef MOTH_THREADED_INTERPRETER
+
+# define MOTH_BEGIN_INSTR(I) op_##I: \
+ MOTH_BEGIN_INSTR_COMMON(I)
+
+# define MOTH_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 VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context,
+ VM::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<VM::Value *>(&param.value);
+ } else if (param.isArgument()) {
+ VMSTATS(paramIsArg);
+ VM::ExecutionContext *c = context;
+ uint scope = param.scope;
+ while (scope--)
+ c = c->outer;
+ VM::CallContext *cc = static_cast<VM::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;
+ VM::CallContext *c = static_cast<VM::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);
+ VM::ExecutionContext *c = context;
+ uint scope = param.scope;
+ while (scope--)
+ c = c->outer;
+ const unsigned index = param.index;
+ VM::CallContext *cc = static_cast<VM::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<VM::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
+
+VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code,
+ VM::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 VM::Value::undefinedValue();
+ }
+#endif
+
+ FunctionState state(context, &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<VM::Value *>(alloca(stackSize * sizeof(VM::Value)));
+ state.setStack(stack, stackSize);
+ MOTH_END_INSTR(Push)
+
+ MOTH_BEGIN_INSTR(CallValue)
+#ifdef DO_TRACE_INSTR
+ if (Debugging::Debugger *debugger = context->engine->debugger) {
+ if (VM::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);
+ VM::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);
+ VM::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);
+ VM::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);
+ VM::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_builtin_throw(context, VALUE(instr.arg));
+ MOTH_END_INSTR(CallBuiltinThrow)
+
+ MOTH_BEGIN_INSTR(EnterTry)
+ VALUE(instr.exceptionVar) = VM::Value::undefinedValue();
+ try {
+ const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset;
+ run(context, tryCode, stack, stackSize);
+ code = tryCode;
+ } catch (VM::Exception &ex) {
+ ex.accept(context);
+ VALUE(instr.exceptionVar) = ex.value();
+ try {
+ VM::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 = __qmljs_builtin_pop_scope(catchContext);
+ } catch (VM::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;
+ }
+ }
+ MOTH_END_INSTR(EnterTry)
+
+ MOTH_BEGIN_INSTR(CallBuiltinFinishTry)
+ return VM::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);
+ VM::Value *args = stack + instr.args;
+ __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc);
+ MOTH_END_INSTR(CallBuiltinDefineArray)
+
+ MOTH_BEGIN_INSTR(CreateValue)
+ Q_ASSERT(instr.args + instr.argc <= stackSize);
+ VM::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);
+ VM::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);
+ VM::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(Ret)
+ VM::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
+
+VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code)
+{
+ VME vme;
+ return vme.run(ctxt, code);
+}
diff --git a/src/qml/qml/v4vm/moth/qv4vme_moth_p.h b/src/qml/qml/v4vm/moth/qv4vme_moth_p.h
new file mode 100644
index 0000000000..9eea23901a
--- /dev/null
+++ b/src/qml/qml/v4vm/moth/qv4vme_moth_p.h
@@ -0,0 +1,35 @@
+#ifndef QV4VME_MOTH_P_H
+#define QV4VME_MOTH_P_H
+
+#include "qv4runtime.h"
+#include "qv4instr_moth_p.h"
+
+namespace QQmlJS {
+namespace VM {
+ struct Value;
+}
+
+namespace Moth {
+
+class VME
+{
+public:
+ static VM::Value exec(VM::ExecutionContext *, const uchar *);
+
+#ifdef MOTH_THREADED_INTERPRETER
+ static void **instructionJumpTable();
+#endif
+
+private:
+ VM::Value run(QQmlJS::VM::ExecutionContext *, const uchar *&code,
+ VM::Value *stack = 0, unsigned stackSize = 0
+#ifdef MOTH_THREADED_INTERPRETER
+ , void ***storeJumpTable = 0
+#endif
+ );
+};
+
+} // namespace Moth
+} // namespace QQmlJS
+
+#endif // QV4VME_MOTH_P_H
diff --git a/src/qml/qml/v4vm/qcalculatehash_p.h b/src/qml/qml/v4vm/qcalculatehash_p.h
new file mode 100644
index 0000000000..36c5a6807a
--- /dev/null
+++ b/src/qml/qml/v4vm/qcalculatehash_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtV8 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 CALCULATEHASH_P_H
+#define CALCULATEHASH_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/qstring.h>
+#include "qv4v8.h"
+QT_BEGIN_NAMESPACE
+
+inline uint32_t calculateHash(const quint8* chars, int length)
+{
+ return v8::String::ComputeHash((char *)chars, length);
+}
+
+inline uint32_t calculateHash(const quint16* chars, int length)
+{
+ return v8::String::ComputeHash((uint16_t *)chars, length);
+}
+
+QT_END_NAMESPACE
+
+#endif // CALCULATEHASH_P_H
diff --git a/src/qml/qml/v4vm/qv4_llvm_p.h b/src/qml/qml/v4vm/qv4_llvm_p.h
new file mode 100644
index 0000000000..189fe8586f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4_llvm_p.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.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_V4_EXPORT int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*)(void *));
+
+} // QQmlJS
+
+#endif // QV4_LLVM_P_H
diff --git a/src/qml/qml/v4vm/qv4alloca_p.h b/src/qml/qml/v4vm/qv4alloca_p.h
new file mode 100644
index 0000000000..1e9388c48c
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4alloca_p.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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/v4vm/qv4argumentsobject.cpp b/src/qml/qml/v4vm/qv4argumentsobject.cpp
new file mode 100644
index 0000000000..21c72be460
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4argumentsobject.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+
+namespace QQmlJS {
+namespace VM {
+
+
+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(ctx, 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, ExecutionContext *ctx, const Value &thisObject, Value *, int)
+{
+ ArgumentsGetterFunction *g = static_cast<ArgumentsGetterFunction *>(getter);
+ Object *that = thisObject.asObject();
+ if (!that)
+ ctx->throwTypeError();
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ assert(g->index < o->context->argumentCount);
+ return o->context->argument(g->index);
+}
+
+DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction);
+
+Value ArgumentsSetterFunction::call(Managed *setter, ExecutionContext *ctx, const Value &thisObject, Value *args, int argc)
+{
+ ArgumentsSetterFunction *s = static_cast<ArgumentsSetterFunction *>(setter);
+ Object *that = thisObject.asObject();
+ if (!that)
+ ctx->throwTypeError();
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ ctx->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/v4vm/qv4argumentsobject.h b/src/qml/qml/v4vm/qv4argumentsobject.h
new file mode 100644
index 0000000000..6727d8759b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4argumentsobject.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+#include <qv4functionobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct ArgumentsGetterFunction: FunctionObject
+{
+ uint index;
+
+ ArgumentsGetterFunction(ExecutionContext *scope, uint index)
+ : FunctionObject(scope), index(index) { vtbl = &static_vtbl; }
+
+ static Value call(Managed *that, ExecutionContext *, 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, ExecutionContext *, 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/v4vm/qv4arrayobject.cpp b/src/qml/qml/v4vm/qv4arrayobject.cpp
new file mode 100644
index 0000000000..90746d008c
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4arrayobject.cpp
@@ -0,0 +1,859 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4sparsearray.h"
+
+using namespace QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(ArrayCtor);
+
+ArrayCtor::ArrayCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value ArrayCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc)
+{
+ ArrayObject *a = ctx->engine->newArrayObject(ctx);
+ uint len;
+ if (argc == 1 && argv[0].isNumber()) {
+ bool ok;
+ len = argv[0].asArrayLength(&ok);
+
+ if (!ok)
+ ctx->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, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc)
+{
+ return construct(that, ctx, argv, argc);
+}
+
+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, 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(ctx);
+
+ 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(ctx, 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, ctx->engine->id_length, Value::fromInt32(0));
+ return Value::undefinedValue();
+ }
+
+ Value result = instance->getIndexed(ctx, len - 1);
+
+ instance->deleteIndexedProperty(ctx, len - 1);
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len - 1);
+ else
+ instance->put(ctx, 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(ctx, idx.toString(ctx), ctx->argument(i));
+ }
+ double newLen = l + ctx->argumentCount;
+ if (!instance->isArrayObject())
+ instance->put(ctx, 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(ctx, len + i, ctx->argument(i));
+ len += ctx->argumentCount;
+ }
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len);
+ else
+ instance->put(ctx, 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(ctx, lo, &loExists);
+ Value hval = instance->getIndexed(ctx, hi, &hiExists);
+ if (hiExists)
+ instance->putIndexed(ctx, lo, hval);
+ else
+ instance->deleteIndexedProperty(ctx, lo);
+ if (loExists)
+ instance->putIndexed(ctx, hi, lval);
+ else
+ instance->deleteIndexedProperty(ctx, 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, 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(ctx, 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(ctx, k, &exists);
+ if (exists)
+ instance->putIndexed(ctx, k - 1, v);
+ else
+ instance->deleteIndexedProperty(ctx, k - 1);
+ }
+ instance->deleteIndexedProperty(ctx, len - 1);
+ }
+
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(len - 1);
+ else
+ instance->put(ctx, 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(ctx);
+ uint len = o->get(ctx, 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(ctx, 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(ctx);
+
+ 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(ctx, 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(ctx, k + deleteCount, &exists);
+ if (exists)
+ instance->arraySet(k + itemCount, v);
+ else
+ instance->deleteIndexedProperty(ctx, k + itemCount);
+ }
+ for (uint k = len; k > len - deleteCount + itemCount; --k)
+ instance->deleteIndexedProperty(ctx, k - 1);
+ } else if (itemCount > deleteCount) {
+ uint k = len - deleteCount;
+ while (k > start) {
+ bool exists;
+ Value v = instance->getIndexed(ctx, k + deleteCount - 1, &exists);
+ if (exists)
+ instance->arraySet(k + itemCount - 1, v);
+ else
+ instance->deleteIndexedProperty(ctx, k + itemCount - 1);
+ --k;
+ }
+ }
+
+ for (uint i = 0; i < itemCount; ++i)
+ instance->arraySet(start + i, ctx->argument(i + 2));
+
+ ctx->strictMode = true;
+ instance->put(ctx, 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(ctx, k - 1, &exists);
+ if (exists)
+ instance->putIndexed(ctx, k + ctx->argumentCount - 1, v);
+ else
+ instance->deleteIndexedProperty(ctx, k + ctx->argumentCount - 1);
+ }
+ for (uint i = 0; i < ctx->argumentCount; ++i)
+ instance->putIndexed(ctx, i, ctx->argument(i));
+ }
+
+ uint newLen = len + ctx->argumentCount;
+ if (instance->isArrayObject())
+ instance->setArrayLengthUnchecked(newLen);
+ else
+ instance->put(ctx, 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(ctx, 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(ctx, 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(ctx, k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value r = callback->call(ctx, 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(ctx, k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value r = callback->call(ctx, 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(ctx, k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ callback->call(ctx, 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(ctx);
+ a->arrayReserve(len);
+ a->setArrayLengthUnchecked(len);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(ctx, k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value mapped = callback->call(ctx, 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(ctx);
+ a->arrayReserve(len);
+
+ uint to = 0;
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->getIndexed(ctx, k, &exists);
+ if (!exists)
+ continue;
+
+ Value args[3];
+ args[0] = v;
+ args[1] = Value::fromDouble(k);
+ args[2] = ctx->thisObject;
+ Value selected = callback->call(ctx, 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(ctx, k, &kPresent);
+ if (kPresent)
+ acc = v;
+ ++k;
+ }
+ if (!kPresent)
+ ctx->throwTypeError();
+ }
+
+ while (k < len) {
+ bool kPresent;
+ Value v = instance->getIndexed(ctx, 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(ctx, 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(ctx, k - 1, &kPresent);
+ if (kPresent)
+ acc = v;
+ --k;
+ }
+ if (!kPresent)
+ ctx->throwTypeError();
+ }
+
+ while (k > 0) {
+ bool kPresent;
+ Value v = instance->getIndexed(ctx, 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(ctx, Value::undefinedValue(), args, 4);
+ }
+ --k;
+ }
+ return acc;
+}
+
diff --git a/src/qml/qml/v4vm/qv4arrayobject.h b/src/qml/qml/v4vm/qv4arrayobject.h
new file mode 100644
index 0000000000..86d14eb587
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4arrayobject.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+
+struct ArrayCtor: FunctionObject
+{
+ ArrayCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ArrayPrototype: ArrayObject
+{
+ ArrayPrototype(ExecutionContext *context) : ArrayObject(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);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4booleanobject.cpp b/src/qml/qml/v4vm/qv4booleanobject.cpp
new file mode 100644
index 0000000000..e449e5c7db
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4booleanobject.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+using namespace QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(BooleanCtor);
+
+BooleanCtor::BooleanCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value BooleanCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ bool n = argc ? args[0].toBoolean() : false;
+ return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n)));
+}
+
+Value BooleanCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, 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/v4vm/qv4booleanobject.h b/src/qml/qml/v4vm/qv4booleanobject.h
new file mode 100644
index 0000000000..05b542590a
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4booleanobject.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct BooleanCtor: FunctionObject
+{
+ BooleanCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4codegen.cpp b/src/qml/qml/v4vm/qv4codegen.cpp
new file mode 100644
index 0000000000..c91458282f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4codegen.cpp
@@ -0,0 +1,3256 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "debugging.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+#include <QtCore/QBuffer>
+#include <QtCore/QBitArray>
+#include <QtCore/QStack>
+#include <private/qqmljsast_p.h>
+#include <qv4runtime.h>
+#include <qv4context.h>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+#ifdef CONST
+#undef CONST
+#endif
+
+using namespace QQmlJS;
+using namespace AST;
+
+namespace {
+QTextStream qout(stdout, QIODevice::WriteOnly);
+
+void dfs(V4IR::BasicBlock *block,
+ QSet<V4IR::BasicBlock *> *V,
+ QVector<V4IR::BasicBlock *> *blocks)
+{
+ if (! V->contains(block)) {
+ V->insert(block);
+
+ foreach (V4IR::BasicBlock *succ, block->out)
+ dfs(succ, V, blocks);
+
+ blocks->append(block);
+ }
+}
+
+struct ComputeUseDef: V4IR::StmtVisitor, V4IR::ExprVisitor
+{
+ V4IR::Function *_function;
+ V4IR::Stmt *_stmt;
+
+ ComputeUseDef(V4IR::Function *function)
+ : _function(function)
+ , _stmt(0) {}
+
+ void operator()(V4IR::Stmt *s) {
+ assert(! s->d);
+ s->d = new V4IR::Stmt::Data;
+ qSwap(_stmt, s);
+ _stmt->accept(this);
+ qSwap(_stmt, s);
+ }
+
+ virtual void visitConst(V4IR::Const *) {}
+ virtual void visitString(V4IR::String *) {}
+ virtual void visitRegExp(V4IR::RegExp *) {}
+ virtual void visitName(V4IR::Name *) {}
+ virtual void visitClosure(V4IR::Closure *) {}
+ 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 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 visitEnter(V4IR::Enter *) {}
+ virtual void visitLeave(V4IR::Leave *) {}
+ 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 *t) {
+ if (! _stmt->d->defs.contains(t->exceptionVar->index))
+ _stmt->d->defs.append(t->exceptionVar->index);
+ }
+
+ virtual void visitTemp(V4IR::Temp *e) {
+ if (e->index < 0 || e->scope != 0)
+ return;
+
+ if (! _stmt->d->uses.contains(e->index))
+ _stmt->d->uses.append(e->index);
+ }
+
+ 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 visitMove(V4IR::Move *s) {
+ if (V4IR::Temp *t = s->target->asTemp()) {
+ if (t->index >= 0 && t->scope == 0) // only collect unscoped locals and temps
+ if (! _stmt->d->defs.contains(t->index))
+ _stmt->d->defs.append(t->index);
+ } else {
+ // source was not a temp, but maybe a sub-expression has a temp
+ // (e.g. base expressions for subscripts/member-access),
+ // so visit it.
+ s->target->accept(this);
+ }
+ // whatever the target expr was, always visit the source expr to collect
+ // temps there.
+ s->source->accept(this);
+ }
+};
+
+void liveness(V4IR::Function *function)
+{
+ QSet<V4IR::BasicBlock *> V;
+ QVector<V4IR::BasicBlock *> blocks;
+
+ ComputeUseDef computeUseDef(function);
+ foreach (V4IR::BasicBlock *block, function->basicBlocks) {
+ foreach (V4IR::Stmt *s, block->statements)
+ computeUseDef(s);
+ }
+
+ dfs(function->basicBlocks.at(0), &V, &blocks);
+
+ bool changed;
+ do {
+ changed = false;
+
+ foreach (V4IR::BasicBlock *block, blocks) {
+ const QBitArray previousLiveIn = block->liveIn;
+ const QBitArray previousLiveOut = block->liveOut;
+ QBitArray live(function->tempCount);
+ foreach (V4IR::BasicBlock *succ, block->out)
+ live |= succ->liveIn;
+ block->liveOut = live;
+ for (int i = block->statements.size() - 1; i != -1; --i) {
+ V4IR::Stmt *s = block->statements.at(i);
+ s->d->liveOut = live;
+ foreach (unsigned d, s->d->defs)
+ live.clearBit(d);
+ foreach (unsigned u, s->d->uses)
+ live.setBit(u);
+ s->d->liveIn = live;
+ }
+ block->liveIn = live;
+ if (! changed) {
+ if (previousLiveIn != block->liveIn || previousLiveOut != block->liveOut)
+ changed = true;
+ }
+ }
+ } while (changed);
+}
+
+static inline bool isDeadAssignment(V4IR::Stmt *stmt, int localCount)
+{
+ V4IR::Move *move = stmt->asMove();
+ if (!move || move->op != V4IR::OpInvalid)
+ return false;
+ V4IR::Temp *target = move->target->asTemp();
+ if (!target)
+ return false;
+ if (target->scope || target->index < localCount)
+ return false;
+
+ if (V4IR::Name *n = move->source->asName()) {
+ if (*n->id != QStringLiteral("this"))
+ return false;
+ } else if (!move->source->asConst() && !move->source->asTemp()) {
+ return false;
+ }
+
+ return !stmt->d->liveOut.at(target->index);
+}
+
+void removeDeadAssignments(V4IR::Function *function)
+{
+ const int localCount = function->locals.size();
+ foreach (V4IR::BasicBlock *bb, function->basicBlocks) {
+ QVector<V4IR::Stmt *> &statements = bb->statements;
+ for (int i = 0; i < statements.size(); ) {
+// qout<<"removeDeadAssignments: considering ";statements.at(i)->dump(qout);qout<<"\n";qout.flush();
+ if (isDeadAssignment(statements.at(i), localCount))
+ statements.remove(i);
+ else
+ ++i;
+ }
+ }
+}
+
+class ConstantPropagation: public V4IR::StmtVisitor, public V4IR::ExprVisitor
+{
+ struct Value {
+ enum Type {
+ InvalidType = 0,
+ UndefinedType,
+ NullType,
+ BoolType,
+ NumberType,
+ ThisType,
+ StringType
+ } type;
+
+ union {
+ double numberValue;
+ V4IR::String *stringValue;
+ };
+
+ Value()
+ : type(InvalidType), stringValue(0)
+ {}
+
+ explicit Value(V4IR::String *str)
+ : type(StringType), stringValue(str)
+ {}
+
+ explicit Value(Type t)
+ : type(t), stringValue(0)
+ {}
+
+ Value(Type t, double val)
+ : type(t), numberValue(val)
+ {}
+
+ bool isValid() const
+ { return type != InvalidType; }
+
+ bool operator<(const Value &other) const
+ {
+ if (type < other.type)
+ return true;
+ if (type == Value::NumberType && other.type == Value::NumberType) {
+ if (numberValue == 0 && other.numberValue == 0)
+ return isNegative(numberValue) && !isNegative(other.numberValue);
+ else
+ return numberValue < other.numberValue;
+ }
+ if (type == Value::BoolType && other.type == Value::BoolType)
+ return numberValue < other.numberValue;
+ if (type == Value::StringType && other.type == Value::StringType)
+ return *stringValue->value < *other.stringValue->value;
+ return false;
+ }
+
+ bool operator==(const Value &other) const
+ {
+ if (type != other.type)
+ return false;
+ if (type == Value::NumberType && other.type == Value::NumberType) {
+ if (numberValue == 0 && other.numberValue == 0)
+ return isNegative(numberValue) == isNegative(other.numberValue);
+ else
+ return numberValue == other.numberValue;
+ }
+ if (type == Value::BoolType && other.type == Value::BoolType)
+ return numberValue == other.numberValue;
+ if (type == Value::StringType && other.type == Value::StringType)
+ return *stringValue->value == *other.stringValue->value;
+ return false;
+ }
+ };
+
+public:
+ void run(V4IR::Function *function)
+ {
+ if (function->hasTry)
+ return;
+ localCount = function->locals.size();
+ if (function->hasWith) {
+ thisTemp = -1;
+ } else {
+ V4IR::BasicBlock *entryBlock = function->basicBlocks.at(0);
+ thisTemp = entryBlock->newTemp();
+ V4IR::Move *fetchThis = function->New<V4IR::Move>();
+ fetchThis->init(entryBlock->TEMP(thisTemp),
+ entryBlock->NAME(QStringLiteral("this"), 0, 0),
+ V4IR::OpInvalid);
+ entryBlock->statements.prepend(fetchThis);
+ }
+
+ foreach (V4IR::BasicBlock *block, function->basicBlocks) {
+// qDebug()<<"--- Starting with BB"<<block->index;
+ reset();
+ QVector<V4IR::Stmt *> &statements = block->statements;
+ foreach (V4IR::Stmt *stmt, statements) {
+// qout<<"*** ";stmt->dump(qout);qout<<"\n";qout.flush();
+ stmt->accept(this);
+ }
+ }
+ }
+
+protected:
+ virtual void visitConst(V4IR::Const *) {}
+ virtual void visitString(V4IR::String *) {}
+ virtual void visitRegExp(V4IR::RegExp *) {}
+ virtual void visitName(V4IR::Name *) {}
+ virtual void visitClosure(V4IR::Closure *) {}
+ 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 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 visitEnter(V4IR::Enter *) {}
+ virtual void visitLeave(V4IR::Leave *) {}
+ 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 *) {}
+
+ 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 visitTemp(V4IR::Temp *e) {
+ if (e->scope)
+ return;
+
+ const int replacement = tempReplacement.value(e->index, -1);
+ if (replacement != -1) {
+// qDebug() << "+++ Replacing" << e->index << "with" << replacement;
+ e->index = replacement;
+ }
+ }
+
+ virtual void visitMove(V4IR::Move *s) {
+ V4IR::Temp *targetTemp = s->target->asTemp();
+ if (targetTemp && targetTemp->index >= localCount && !targetTemp->scope) {
+ if (s->op == V4IR::OpInvalid) {
+ if (V4IR::Name *n = s->source->asName()) {
+ if (thisTemp != -1) {
+ if (*n->id == QStringLiteral("this")) {
+ check(targetTemp->index, Value(Value::ThisType));
+ return;
+ }
+ }
+ } else if (V4IR::Const *c = s->source->asConst()) {
+ Value value;
+ switch (c->type) {
+ case V4IR::UndefinedType: value.type = Value::UndefinedType; break;
+ case V4IR::NullType: value.type = Value::NullType; break;
+ case V4IR::BoolType: value.type = Value::BoolType; value.numberValue = c->value == 0 ? 0 : 1; break;
+ case V4IR::NumberType: value.type = Value::NumberType; value.numberValue = c->value; break;
+ default: Q_ASSERT("unknown const type"); return;
+ }
+ check(targetTemp->index, value);
+ return;
+ } else if (V4IR::String *str = s->source->asString()) {
+ check(targetTemp->index, Value(str));
+ return;
+ }
+ }
+ invalidate(targetTemp->index, Value());
+ } else {
+ s->target->accept(this);
+ }
+
+ s->source->accept(this);
+ }
+
+ void invalidate(int &targetTempIndex, const Value &value)
+ {
+ QMap<int, Value>::iterator it = valueForTemp.find(targetTempIndex);
+ if (it != valueForTemp.end()) {
+ if (it.value() == value)
+ return;
+ tempForValue.remove(it.value());
+ valueForTemp.erase(it);
+ }
+
+ QMap<int, int>::iterator it2 = tempReplacement.find(targetTempIndex);
+ if (it2 != tempReplacement.end()) {
+ tempReplacement.erase(it2);
+ }
+ }
+
+ void check(int &targetTempIndex, const Value &value)
+ {
+ Q_ASSERT(value.isValid());
+
+ invalidate(targetTempIndex, value);
+
+ int replacementTemp = tempForValue.value(value, -1);
+ if (replacementTemp == -1) {
+// qDebug() << "+++ inserting temp" << targetTempIndex;
+ tempForValue.insert(value, targetTempIndex);
+ valueForTemp.insert(targetTempIndex, value);
+ } else {
+// qDebug() << "+++ temp" << targetTempIndex << "can be replaced with" << replacementTemp;
+ tempReplacement.insert(targetTempIndex, replacementTemp);
+ }
+ }
+
+ void reset()
+ {
+ tempForValue.clear();
+ tempReplacement.clear();
+ if (thisTemp != -1)
+ tempForValue.insert(Value(Value::ThisType), thisTemp);
+ }
+
+private:
+ QMap<Value, int> tempForValue;
+ QMap<int, Value> valueForTemp;
+ QMap<int, int> tempReplacement;
+
+ int localCount;
+ int thisTemp;
+};
+
+#undef DEBUG_TEMP_COMPRESSION
+#ifdef DEBUG_TEMP_COMPRESSION
+# define DBTC(x) x
+#else // !DEBUG_TEMP_COMPRESSION
+# define DBTC(x)
+#endif // DEBUG_TEMP_COMPRESSION
+class CompressTemps: public V4IR::StmtVisitor, V4IR::ExprVisitor
+{
+public:
+ void run(V4IR::Function *function)
+ {
+ _nextFree = 0;
+ _active.reserve(function->tempCount);
+ _localCount = function->locals.size();
+
+ DBTC(qDebug() << "starting on function" << (*function->name) << "with" << (function->tempCount - _localCount) << "temps.";)
+
+ QVector<int> pinned;
+ foreach (V4IR::BasicBlock *block, function->basicBlocks) {
+ if (V4IR::Stmt *last = block->terminator()) {
+ const QBitArray &liveOut = last->d->liveOut;
+ for (int i = _localCount, ei = liveOut.size(); i < ei; ++i) {
+ if (liveOut.at(i) && !pinned.contains(i)) {
+ pinned.append(i);
+ add(i - _localCount, _nextFree);
+ }
+ }
+ }
+ }
+ _pinnedCount = _nextFree;
+
+ int maxUsed = _nextFree;
+
+ foreach (V4IR::BasicBlock *block, function->basicBlocks) {
+ DBTC(qDebug("L%d:", block->index));
+
+ for (int i = 0, ei = block->statements.size(); i < ei; ++i ) {
+ _currentStatement = block->statements[i];
+ if (i == 0)
+ expireOld();
+
+ DBTC(_currentStatement->dump(qout);qout<<endl<<flush;)
+
+ if (_currentStatement->d)
+ _currentStatement->accept(this);
+ }
+ maxUsed = std::max(maxUsed, _nextFree);
+ }
+ DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";)
+ function->tempCount = maxUsed + _localCount;
+ }
+
+protected:
+ virtual void visitConst(V4IR::Const *) {}
+ virtual void visitString(V4IR::String *) {}
+ virtual void visitRegExp(V4IR::RegExp *) {}
+ virtual void visitName(V4IR::Name *) {}
+ virtual void visitClosure(V4IR::Closure *) {}
+ 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 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 visitEnter(V4IR::Enter *) {}
+ virtual void visitLeave(V4IR::Leave *) {}
+ 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 *t) { visitTemp(t->exceptionVar); }
+
+ virtual void visitTemp(V4IR::Temp *e) {
+ if (e->scope) // scoped local
+ return;
+ if (e->index < _localCount) // local or argument
+ return;
+
+ e->index = remap(e->index - _localCount) + _localCount;
+ }
+
+ 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 visitMove(V4IR::Move *s) {
+ s->target->accept(this);
+ s->source->accept(this);
+ }
+
+private:
+ int remap(int tempIndex) {
+ for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) {
+ if (i->first == tempIndex) {
+ DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);)
+ return i->second;
+ }
+ }
+
+ int firstFree = expireOld();
+ add(tempIndex, firstFree);
+ return firstFree;
+ }
+
+ void add(int tempIndex, int firstFree) {
+ if (_nextFree <= firstFree)
+ _nextFree = firstFree + 1;
+ _active.prepend(qMakePair(tempIndex, firstFree));
+ DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);)
+ }
+
+ int expireOld() {
+ Q_ASSERT(_currentStatement->d);
+
+ const QBitArray &liveIn = _currentStatement->d->liveIn;
+ QBitArray inUse(_nextFree);
+ int i = 0;
+ while (i < _active.size()) {
+ const QPair<int, int> &p = _active[i];
+
+ if (p.second < _pinnedCount) {
+ inUse.setBit(p.second);
+ ++i;
+ continue;
+ }
+
+ if (liveIn[p.first + _localCount]) {
+ inUse[p.second] = true;
+ ++i;
+ } else {
+ DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);)
+ _active.remove(i);
+ }
+ }
+ for (int i = 0, ei = inUse.size(); i < ei; ++i)
+ if (!inUse[i])
+ return i;
+ return _nextFree;
+ }
+
+private:
+ typedef QVector<QPair<int, int> > ActiveTemps;
+ ActiveTemps _active;
+ V4IR::Stmt *_currentStatement;
+ int _localCount;
+ int _nextFree;
+ int _pinnedCount;
+};
+#undef DBTC
+
+} // end of anonymous namespace
+
+class Codegen::ScanFunctions: Visitor
+{
+ typedef 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)
+ {
+ 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;
+ _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(VM::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)
+ , _debugger(context->engine->debugger)
+ , _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)
+ , _debugger(0)
+ , _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);
+ if (_debugger) {
+ if (node->elements->element) {
+ SourceLocation loc = node->elements->element->firstSourceLocation();
+ _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn);
+ }
+ }
+
+ foreach (V4IR::Function *function, _module->functions) {
+ linearize(function);
+ }
+
+ 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);
+ if (_debugger)
+ _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
+
+ foreach (V4IR::Function *function, _module->functions) {
+ linearize(function);
+ }
+
+ 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 *breakBlock, V4IR::BasicBlock *continueBlock)
+{
+ _loop = new Loop(node, breakBlock, continueBlock, _loop);
+ _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement
+ _loop->scopeAndFinally = _scopeAndFinally;
+ _labelledStatement = 0;
+}
+
+void Codegen::leaveLoop()
+{
+ Loop *current = _loop;
+ _loop = _loop->parent;
+ delete current;
+}
+
+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)
+{
+ 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, ~VM::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, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f));
+ case V4IR::OpMod: return _block->CONST(V4IR::NumberType, ::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, VM::Value::toInt32(c1->value) >> (VM::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,VM::Value::toUInt32(c1->value) >> (VM::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());
+
+ 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)
+{
+ 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->TEMP(index), 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();
+ condition(ast->left, iftrue, _expr.iffalse);
+ _block = iftrue;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ V4IR::BasicBlock *iftrue = _function->newBasicBlock();
+ V4IR::BasicBlock *endif = _function->newBasicBlock();
+
+ 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();
+ condition(ast->left, _expr.iftrue, iffalse);
+ _block = iffalse;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock();
+ V4IR::BasicBlock *endif = _function->newBasicBlock();
+
+ 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();
+ V4IR::BasicBlock *iffalse = _function->newBasicBlock();
+ V4IR::BasicBlock *endif = _function->newBasicBlock();
+
+ 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);
+ if (_debugger)
+ _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn);
+ _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->TEMP(index, scope);
+ }
+ const int argIdx = f->indexOfArgument(&name);
+ if (argIdx != -1)
+ return _block->TEMP(-(argIdx + 1), scope);
+ ++scope;
+ e = e->parent;
+ f = f->outer;
+ }
+
+ if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && (!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;
+
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn)));
+ 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);
+ if (_debugger)
+ _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn);
+ 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();
+ }
+ }
+ 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, unop(V4IR::OpDecrement, *expr));
+ } 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, unop(V4IR::OpIncrement, *expr));
+ } 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);
+ move(*expr, unop(V4IR::OpDecrement, *expr));
+ if (_expr.accept(nx)) {
+ // nothing to do
+ } else {
+ _expr.code = *expr;
+ }
+ return false;
+}
+
+bool Codegen::visit(PreIncrementExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken);
+ move(*expr, unop(V4IR::OpIncrement, *expr));
+ if (_expr.accept(nx)) {
+ // nothing to do
+ } else {
+ _expr.code = *expr;
+ }
+ 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*/)
+{
+ _expr.accept(nx);
+ return false;
+}
+
+void Codegen::linearize(V4IR::Function *function)
+{
+ V4IR::BasicBlock *exitBlock = function->basicBlocks.last();
+ assert(exitBlock->isTerminated());
+ assert(exitBlock->terminator()->asRet());
+
+ QSet<V4IR::BasicBlock *> V;
+ V.insert(exitBlock);
+
+ QVector<V4IR::BasicBlock *> trace;
+
+ for (int i = 0; i < function->basicBlocks.size(); ++i) {
+ V4IR::BasicBlock *block = function->basicBlocks.at(i);
+ if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) {
+ V4IR::BasicBlock *next = function->basicBlocks.at(i + 1);
+ block->JUMP(next);
+ }
+ }
+
+ struct I { static void trace(V4IR::BasicBlock *block, QSet<V4IR::BasicBlock *> *V,
+ QVector<V4IR::BasicBlock *> *output) {
+ if (block == 0 || V->contains(block))
+ return;
+
+ V->insert(block);
+ block->index = output->size();
+ output->append(block);
+
+ if (V4IR::Stmt *term = block->terminator()) {
+ if (V4IR::Jump *j = term->asJump()) {
+ trace(j->target, V, output);
+ } else if (V4IR::CJump *cj = term->asCJump()) {
+ if (! V->contains(cj->iffalse))
+ trace(cj->iffalse, V, output);
+ else
+ trace(cj->iftrue, V, output);
+ } else if (V4IR::Try *t = term->asTry()) {
+ trace(t->tryBlock, V, output);
+ trace(t->catchBlock, V, output);
+ }
+ }
+
+ // We could do this for each type above, but it is safer to have a
+ // "catchall" here
+ for (int ii = 0; ii < block->out.count(); ++ii)
+ trace(block->out.at(ii), V, output);
+ }
+ };
+
+ I::trace(function->basicBlocks.first(), &V, &trace);
+
+ V.insert(exitBlock);
+ exitBlock->index = trace.size();
+ trace.append(exitBlock);
+
+ QVarLengthArray<V4IR::BasicBlock*> blocksToDelete;
+ foreach (V4IR::BasicBlock *b, function->basicBlocks) {
+ if (!V.contains(b)) {
+ foreach (V4IR::BasicBlock *out, b->out) {
+ int idx = out->in.indexOf(b);
+ if (idx >= 0)
+ out->in.remove(idx);
+ }
+ blocksToDelete.append(b);
+ }
+ }
+ qDeleteAll(blocksToDelete);
+ function->basicBlocks = trace;
+
+ function->removeSharedExpressions();
+
+ if (qgetenv("NO_OPT").isEmpty())
+ ConstantPropagation().run(function);
+
+#ifndef QV4_NO_LIVENESS
+ liveness(function);
+#endif
+
+ if (qgetenv("NO_OPT").isEmpty())
+ removeDeadAssignments(function);
+
+ static bool showCode = !qgetenv("SHOW_CODE").isNull();
+ if (showCode) {
+ QVector<V4IR::Stmt *> code;
+ QHash<V4IR::Stmt *, V4IR::BasicBlock *> leader;
+
+ foreach (V4IR::BasicBlock *block, function->basicBlocks) {
+ leader.insert(block->statements.first(), block);
+ foreach (V4IR::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) {
+ V4IR::Stmt *s = code.at(i);
+
+ if (V4IR::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 (V4IR::BasicBlock *in, bb->in)
+ qout << " L" << in->index;
+ qout << endl;
+ }
+ V4IR::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);
+ s->dump(out, V4IR::Stmt::MIR);
+ out.flush();
+
+#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 << " goto L" << s->asCJump()->iffalse << ";" << endl;
+ }
+ }
+
+ qout << "}" << endl
+ << endl;
+ }
+
+ //### NOTE: after this pass, the liveness information is not correct anymore!
+ if (qgetenv("NO_OPT").isEmpty())
+ CompressTemps().run(function);
+}
+
+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.
+
+ ScopeAndFinally *scopeAndFinally = 0;
+
+ enterEnvironment(ast);
+ V4IR::Function *function = _module->newFunction(name, _function);
+
+ if (_debugger)
+ _debugger->addFunction(function);
+ V4IR::BasicBlock *entryBlock = function->newBasicBlock();
+ V4IR::BasicBlock *exitBlock = function->newBasicBlock(V4IR::Function::DontInsertBlock);
+ V4IR::BasicBlock *throwBlock = function->newBasicBlock();
+ 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) {
+ for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) {
+ const QString &local = it.key();
+ function->LOCAL(local);
+ unsigned t = entryBlock->newTemp();
+ (*it).index = 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);
+ 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);
+ Loop *loop = 0;
+
+ qSwap(_function, function);
+ qSwap(_block, entryBlock);
+ qSwap(_exitBlock, exitBlock);
+ qSwap(_throwBlock, throwBlock);
+ qSwap(_returnAddress, returnAddress);
+ qSwap(_scopeAndFinally, scopeAndFinally);
+ qSwap(_loop, loop);
+
+ 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 (_debugger)
+ _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn);
+ 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->TEMP(member.index), _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();
+ V4IR::BasicBlock *loopcond = _function->newBasicBlock();
+ V4IR::BasicBlock *loopend = _function->newBasicBlock();
+
+ enterLoop(ast, 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) {
+ 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();
+ V4IR::BasicBlock *foreachbody = _function->newBasicBlock();
+ V4IR::BasicBlock *foreachend = _function->newBasicBlock();
+
+ enterLoop(ast, 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();
+ V4IR::BasicBlock *forbody = _function->newBasicBlock();
+ V4IR::BasicBlock *forstep = _function->newBasicBlock();
+ V4IR::BasicBlock *forend = _function->newBasicBlock();
+
+ enterLoop(ast, forend, forstep);
+
+ statement(ast->initialiser);
+ _block->JUMP(forcond);
+
+ _block = forcond;
+ condition(ast->condition, forbody, forend);
+
+ _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();
+ V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0;
+ V4IR::BasicBlock *endif = _function->newBasicBlock();
+ 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)
+{
+ _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();
+ enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0);
+ statement(ast->statement);
+ _block->JUMP(breakBlock);
+ _block = breakBlock;
+ leaveLoop();
+ }
+
+ return false;
+}
+
+bool Codegen::visit(LocalForEachStatement *ast)
+{
+ V4IR::BasicBlock *foreachin = _function->newBasicBlock();
+ V4IR::BasicBlock *foreachbody = _function->newBasicBlock();
+ V4IR::BasicBlock *foreachend = _function->newBasicBlock();
+
+ enterLoop(ast, 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();
+ V4IR::BasicBlock *forbody = _function->newBasicBlock();
+ V4IR::BasicBlock *forstep = _function->newBasicBlock();
+ V4IR::BasicBlock *forend = _function->newBasicBlock();
+
+ enterLoop(ast, forend, forstep);
+
+ variableDeclarationList(ast->declarations);
+ _block->JUMP(forcond);
+
+ _block = forcond;
+ condition(ast->condition, forbody, forend);
+
+ _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)
+ 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();
+
+ if (ast->block) {
+ Result lhs = expression(ast->expression);
+ V4IR::BasicBlock *switchcond = _block;
+
+ QHash<Node *, V4IR::BasicBlock *> blockMap;
+
+ enterLoop(ast, switchend, 0);
+
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+
+ _block = _function->newBasicBlock();
+ blockMap[clause] = _block;
+
+ for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
+ statement(it2->statement);
+ }
+
+ if (ast->block->defaultClause) {
+ _block = _function->newBasicBlock();
+ blockMap[ast->block->defaultClause] = _block;
+
+ for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next)
+ statement(it2->statement);
+ }
+
+ for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+
+ _block = _function->newBasicBlock();
+ blockMap[clause] = _block;
+
+ for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
+ statement(it2->statement);
+ }
+
+ 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();
+ 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();
+ 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();
+ V4IR::BasicBlock *catchBody = _function->newBasicBlock();
+ // We always need a finally body to clean up the exception handler
+ V4IR::BasicBlock *finallyBody = _function->newBasicBlock();
+
+ V4IR::BasicBlock *throwBlock = _function->newBasicBlock();
+ 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();
+ _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();
+ _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();
+ _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();
+ V4IR::BasicBlock *whilebody = _function->newBasicBlock();
+ V4IR::BasicBlock *whileend = _function->newBasicBlock();
+
+ enterLoop(ast, 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();
+
+ _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();
+ _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)
+{
+ VM::DiagnosticMessage *msg = new VM::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(VM::Value::fromString(_context, detail));
+ else if (_errorHandler)
+ throwSyntaxError(loc, detail);
+ else
+ Q_ASSERT(!"No error handler available.");
+}
diff --git a/src/qml/qml/v4vm/qv4codegen_p.h b/src/qml/qml/v4vm/qv4codegen_p.h
new file mode 100644
index 0000000000..031e75d207
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4codegen_p.h
@@ -0,0 +1,444 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+#include <QtCore/QStringList>
+#include <assert.h>
+
+namespace QQmlJS {
+
+namespace AST {
+class UiParameterList;
+}
+
+namespace VM {
+struct DiagnosticMessage;
+struct ExecutionContext;
+}
+
+namespace Debugging {
+class Debugger;
+} // namespace Debugging
+
+class ErrorHandler
+{
+public:
+ virtual void syntaxError(VM::DiagnosticMessage *message) = 0;
+};
+
+class Q_V4_EXPORT Codegen: protected AST::Visitor
+{
+public:
+ Codegen(VM::ExecutionContext *ctx, bool strict);
+ Codegen(ErrorHandler *errorHandler, bool strictMode);
+
+ enum Mode {
+ GlobalCode,
+ EvalCode,
+ FunctionCode
+ };
+
+ 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 *breakBlock;
+ V4IR::BasicBlock *continueBlock;
+ Loop *parent;
+ ScopeAndFinally *scopeAndFinally;
+
+ Loop(AST::Statement *node, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent)
+ : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {}
+ };
+
+ void enterEnvironment(AST::Node *node);
+ void leaveEnvironment();
+
+ void enterLoop(AST::Statement *node, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock);
+ void leaveLoop();
+
+
+ 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);
+
+ void linearize(V4IR::Function *function);
+ 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;
+ VM::ExecutionContext *_context;
+ bool _strictMode;
+ Debugging::Debugger *_debugger;
+ ErrorHandler *_errorHandler;
+
+ class ScanFunctions;
+};
+
+} // end of namespace QQmlJS
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/qml/v4vm/qv4context.cpp b/src/qml/qml/v4vm/qv4context.cpp
new file mode 100644
index 0000000000..e1ce0016dd
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4context.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "debugging.h"
+#include <qv4context.h>
+#include <qv4object.h>
+#include <qv4objectproto.h>
+#include "qv4mm.h"
+#include <qv4argumentsobject.h>
+
+namespace QQmlJS {
+namespace VM {
+
+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 == QQmlJS::VM::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::init(ExecutionEngine *eng)
+{
+ type = Type_GlobalContext;
+ strictMode = false;
+ marked = false;
+ thisObject = Value::fromObject(eng->globalObject);
+ engine = eng;
+ outer = 0;
+ lookups = 0;
+ global = 0;
+}
+
+void WithContext::init(ExecutionContext *p, Object *with)
+{
+ type = Type_WithContext;
+ strictMode = false;
+ marked = false;
+ thisObject = p->thisObject;
+ engine = p->engine;
+ outer = p;
+ lookups = p->lookups;
+
+ withObject = with;
+}
+
+void CatchContext::init(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue)
+{
+ type = Type_CatchContext;
+ strictMode = p->strictMode;
+ marked = false;
+ thisObject = p->thisObject;
+ engine = p->engine;
+ outer = p;
+ lookups = p->lookups;
+
+ this->exceptionVarName = exceptionVarName;
+ this->exceptionValue = exceptionValue;
+}
+
+void CallContext::initCallContext(ExecutionEngine *engine)
+{
+ type = Type_CallContext;
+ strictMode = function->strictMode;
+ marked = false;
+ this->engine = engine;
+ 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;
+ Value arguments = Value::fromObject(args);
+ activation = engine->newObject();
+ Property desc = Property::fromValue(Value::fromObject(args));
+ activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable);
+ }
+}
+
+
+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(this, 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(this, name);
+ } else if (ctx->type == Type_GlobalContext) {
+ GlobalContext *g = static_cast<GlobalContext *>(ctx);
+ if (g->global->__hasProperty__(name))
+ return g->global->deleteProperty(this, 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) {
+ VM::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(ctx, 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(this, name, value);
+ return;
+ }
+ }
+ }
+ if (strictMode || name->isEqualTo(engine->id_this))
+ throwReferenceError(Value::fromString(name));
+ engine->globalObject->put(this, name, value);
+}
+
+Value ExecutionContext::getProperty(String *name)
+{
+ name->makeIdentifier(this);
+
+ 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(ctx, 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) {
+ VM::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(c, 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(g, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ throwReferenceError(Value::fromString(name));
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyNoThrow(String *name)
+{
+ name->makeIdentifier(this);
+
+ 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(ctx, 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) {
+ VM::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(c, 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(g, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyAndBase(String *name, Object **base)
+{
+ *base = 0;
+ name->makeIdentifier(this);
+
+ 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(ctx, 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) {
+ VM::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(c, 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(g, 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_builtin_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(this, QStringLiteral("Type error"))));
+}
+
+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(this, msg)));
+}
+
+void ExecutionContext::throwRangeError(Value value)
+{
+ String *s = value.toString(this);
+ QString msg = s->toQString() + QStringLiteral(" out of range");
+ throwError(Value::fromObject(engine->newRangeErrorObject(this, msg)));
+}
+
+void ExecutionContext::throwURIError(Value msg)
+{
+ throwError(Value::fromObject(engine->newURIErrorObject(this, msg)));
+}
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/qml/qml/v4vm/qv4context.h b/src/qml/qml/v4vm/qv4context.h
new file mode 100644
index 0000000000..c26cc6bfc5
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4context.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include <qv4runtime.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Value;
+struct Object;
+struct ExecutionEngine;
+struct DeclarativeEnvironment;
+struct Lookup;
+
+struct Q_V4_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 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
+
+ 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 throwReferenceError(Value value);
+ 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 QQmlJS::VM::Value &value, BinOp op);
+ bool deleteProperty(String *name);
+
+ inline Value argument(unsigned int index = 0);
+
+ void mark();
+
+ inline CallContext *asCallContext();
+};
+
+struct SimpleCallContext : public ExecutionContext
+{
+ FunctionObject *function;
+ Value *arguments;
+ unsigned int argumentCount;
+};
+
+struct CallContext : public SimpleCallContext
+{
+ void initCallContext(QQmlJS::VM::ExecutionEngine *engine);
+ bool needsOwnArguments() const;
+
+ Value *locals;
+ Object *activation;
+};
+
+struct GlobalContext : public ExecutionContext
+{
+ void init(ExecutionEngine *e);
+
+ Object *global;
+};
+
+struct CatchContext : public ExecutionContext
+{
+ void init(ExecutionContext *p, String *exceptionVarName, const QQmlJS::VM::Value &exceptionValue);
+
+ String *exceptionVarName;
+ Value exceptionValue;
+};
+
+struct WithContext : public ExecutionContext
+{
+ Object *withObject;
+
+ void init(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;
+}
+
+/* Function *f, int argc */
+#define requiredMemoryForExecutionContect(f, argc) \
+ sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount))
+#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value))
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4dateobject.cpp b/src/qml/qml/v4vm/qv4dateobject.cpp
new file mode 100644
index 0000000000..adeb11f862
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4dateobject.cpp
@@ -0,0 +1,1316 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4objectproto.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+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);
+ if (!_localtime64_s(&tmtm, &tt))
+#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 FromDateTime(const QDateTime &dt)
+{
+ if (!dt.isValid())
+ return qSNaN();
+ QDate date = dt.date();
+ QTime taim = dt.time();
+ int year = date.year();
+ int month = date.month() - 1;
+ int day = date.day();
+ int hours = taim.hour();
+ int mins = taim.minute();
+ int secs = taim.second();
+ int ms = taim.msec();
+ double t = MakeDate(MakeDay(year, month, day),
+ MakeTime(hours, mins, secs, ms));
+ if (dt.timeSpec() == Qt::LocalTime)
+ t = UTC(t);
+ return TimeClip(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;
+ }
+ }
+ return FromDateTime(dt);
+}
+
+/*!
+ \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 (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 (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 (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
+}
+
+DEFINE_MANAGED_VTABLE(DateCtor);
+
+DateCtor::DateCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value DateCtor::construct(Managed *, ExecutionContext *ctx, 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 = ctx->engine->newDateObject(Value::fromDouble(t));
+ return Value::fromObject(d);
+}
+
+Value DateCtor::call(Managed *, ExecutionContext *ctx, const Value &, Value *, int)
+{
+ double t = currentTime();
+ return Value::fromString(ctx, 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 (! isnan(t))
+ t = YearFromTime(LocalTime(t)) - 1900;
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getFullYear(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = YearFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = YearFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMonth(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = MonthFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = MonthFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDate(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = DateFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = DateFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDay(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = WeekDay(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = WeekDay(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getHours(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = HourFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = HourFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMinutes(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = MinFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = MinFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getSeconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = SecFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = SecFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = msFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! isnan(t))
+ t = msFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! 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 (isnan(t))
+ t = 0;
+ else
+ t = LocalTime(t);
+ double year = ctx->argument(0).toNumber();
+ double r;
+ if (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 (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, ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject();
+
+ if (!toIso)
+ ctx->throwTypeError();
+
+ return toIso->call(ctx, ctx->thisObject, 0, 0);
+}
diff --git a/src/qml/qml/v4vm/qv4dateobject.h b/src/qml/qml/v4vm/qv4dateobject.h
new file mode 100644
index 0000000000..49a879e809
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4dateobject.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct DateObject: Object {
+ Value value;
+ DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; }
+};
+
+struct DateCtor: FunctionObject
+{
+ DateCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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);
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4engine.cpp b/src/qml/qml/v4vm/qv4engine.cpp
new file mode 100644
index 0000000000..7d5c79f333
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4engine.cpp
@@ -0,0 +1,551 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+#include <qv4value.h>
+#include <qv4object.h>
+#include <qv4objectproto.h>
+#include <qv4arrayobject.h>
+#include <qv4booleanobject.h>
+#include <qv4globalobject.h>
+#include <qv4errorobject.h>
+#include <qv4functionobject.h>
+#include <qv4mathobject.h>
+#include <qv4numberobject.h>
+#include <qv4regexpobject.h>
+#include <qv4runtime.h>
+#include "qv4mm.h"
+#include <qv4argumentsobject.h>
+#include <qv4dateobject.h>
+#include <qv4jsonobject.h>
+#include <qv4stringobject.h>
+#include <qv4identifier.h>
+#include <qv4unwindhelper.h>
+#include "qv4isel_masm_p.h"
+#include "debugging.h"
+#include "qv4executableallocator.h"
+
+namespace QQmlJS {
+namespace VM {
+
+ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
+ : memoryManager(new QQmlJS::VM::MemoryManager)
+ , executableAllocator(new QQmlJS::VM::ExecutableAllocator)
+ , debugger(0)
+ , globalObject(0)
+ , globalCode(0)
+ , externalResourceComparison(0)
+ , regExpCache(0)
+{
+ MemoryManager::GCBlocker gcBlocker(memoryManager);
+
+ if (!factory)
+ factory = new MASM::ISelFactory;
+ iselFactory.reset(factory);
+
+ memoryManager->setExecutionEngine(this);
+
+ identifierCache = new Identifiers(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"));
+
+ emptyClass = new InternalClass(this);
+ arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable);
+ initRootContext();
+
+ objectPrototype = new (memoryManager) ObjectPrototype(this);
+ stringPrototype = new (memoryManager) StringPrototype(rootContext);
+ 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(rootContext);
+ evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext);
+ rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext);
+ referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext);
+ syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext);
+ typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext);
+ uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext);
+
+ 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(rootContext, 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(rootContext, errorCtor);
+ evalErrorPrototype->init(rootContext, evalErrorCtor);
+ rangeErrorPrototype->init(rootContext, rangeErrorCtor);
+ referenceErrorPrototype->init(rootContext, referenceErrorCtor);
+ syntaxErrorPrototype->init(rootContext, syntaxErrorCtor);
+ typeErrorPrototype->init(rootContext, typeErrorCtor);
+ uRIErrorPrototype->init(rootContext, uRIErrorCtor);
+
+ //
+ // 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 regExpCache;
+ UnwindHelper::deregisterFunctions(functions);
+ qDeleteAll(functions);
+ delete memoryManager;
+ delete executableAllocator;
+}
+
+void ExecutionEngine::initRootContext()
+{
+ rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
+ current = rootContext;
+ current->parent = 0;
+ rootContext->init(this);
+}
+
+WithContext *ExecutionEngine::newWithContext(Object *with)
+{
+ ExecutionContext *p = current;
+ WithContext *w = static_cast<WithContext *>(memoryManager->allocContext(sizeof(WithContext)));
+ w->parent = current;
+ current = w;
+
+ w->init(p, with);
+ return w;
+}
+
+CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue)
+{
+ ExecutionContext *p = current;
+ CatchContext *c = static_cast<CatchContext *>(memoryManager->allocContext(sizeof(CatchContext)));
+ c->parent = current;
+ current = c;
+
+ c->init(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)));
+ c->parent = current;
+ current = c;
+
+ c->function = f;
+ c->thisObject = thisObject;
+ c->arguments = args;
+ c->argumentCount = argc;
+ c->initCallContext(this);
+
+ 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
+ }
+ c->parent = current;
+ current = c;
+
+ c->function = f;
+ c->thisObject = thisObject;
+ c->arguments = args;
+ c->argumentCount = argc;
+ c->initCallContext(this);
+
+ return c;
+}
+
+
+ExecutionContext *ExecutionEngine::pushGlobalContext()
+{
+ GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext)));
+ *g = *rootContext;
+ g->parent = current;
+ current = g;
+
+ return current;
+}
+
+Function *ExecutionEngine::newFunction(const QString &name)
+{
+ VM::Function *f = new VM::Function(newIdentifier(name));
+ functions.append(f);
+ 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, VM::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;
+}
+
+String *ExecutionEngine::newString(const QString &s)
+{
+ return new (memoryManager) String(s);
+}
+
+String *ExecutionEngine::newIdentifier(const QString &text)
+{
+ return identifierCache->insert(text);
+}
+
+Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value)
+{
+ StringObject *object = new (memoryManager) StringObject(ctx, 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;
+}
+
+Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx)
+{
+ Object *object = new (memoryManager) FunctionObject(ctx);
+ object->prototype = functionPrototype;
+ return object;
+}
+
+ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx)
+{
+ ArrayObject *object = new (memoryManager) ArrayObject(ctx);
+ object->prototype = arrayPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newDateObject(const Value &value)
+{
+ Object *object = new (memoryManager) DateObject(this, value);
+ object->prototype = datePrototype;
+ return object;
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
+{
+ bool global = (flags & V4IR::RegExp::RegExp_Global);
+ bool ignoreCase = false;
+ bool multiline = false;
+ if (flags & V4IR::RegExp::RegExp_IgnoreCase)
+ ignoreCase = true;
+ if (flags & 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;
+}
+
+Object *ExecutionEngine::newErrorObject(const Value &value)
+{
+ ErrorObject *object = new (memoryManager) ErrorObject(rootContext, value);
+ object->prototype = errorPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+{
+ return new (memoryManager) SyntaxErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message)
+{
+ return new (memoryManager) ReferenceErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message)
+{
+ return new (memoryManager) TypeErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message)
+{
+ return new (memoryManager) RangeErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message)
+{
+ return new (memoryManager) URIErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o)
+{
+ return new (memoryManager) ForEachIteratorObject(ctx, o);
+}
+
+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()
+{
+ identifierCache->mark();
+
+ globalObject->mark();
+
+ if (globalCode)
+ globalCode->mark();
+
+ for (int i = 0; i < argumentsAccessors.size(); ++i) {
+ const Property &pd = argumentsAccessors.at(i);
+ pd.getter()->mark();
+ pd.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();
+}
+
+Value ExecutionEngine::run(Function *function, ExecutionContext *ctx)
+{
+ if (!ctx)
+ ctx = rootContext;
+
+ TemporaryAssignment<Function*>(globalCode, function);
+
+ // ### Would be better to have a SavedExecutionState object that
+ // saves this and restores it in the destructor (to survive an exception).
+ ctx->strictMode = function->isStrict;
+ ctx->lookups = function->lookups;
+
+ if (debugger)
+ debugger->aboutToCall(0, ctx);
+ QQmlJS::VM::Value result = function->code(ctx, function->codeData);
+ if (debugger)
+ debugger->justLeft(ctx);
+ return result;
+}
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/qml/qml/v4vm/qv4engine.h b/src/qml/qml/v4vm/qv4engine.h
new file mode 100644
index 0000000000..538796eefa
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4engine.h
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4isel_p.h"
+#include "qv4object.h"
+#include "qv4util.h"
+#include "qv4context.h"
+#include "qv4property.h"
+#include <setjmp.h>
+
+#include <wtf/BumpPointerAllocator.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+namespace Debugging {
+class Debugger;
+} // namespace Debugging
+
+namespace VM {
+
+struct Value;
+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 EvalFunction;
+struct Identifiers;
+struct InternalClass;
+
+class RegExp;
+class RegExpCache;
+
+typedef bool (*ExternalResourceComparison)(const VM::Value &a, const VM::Value &b);
+
+struct Q_V4_EXPORT ExecutionEngine
+{
+ MemoryManager *memoryManager;
+ ExecutableAllocator *executableAllocator;
+ QScopedPointer<EvalISelFactory> iselFactory;
+
+ ExecutionContext *current;
+ GlobalContext *rootContext;
+
+ WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine.
+
+ Identifiers *identifierCache;
+
+ Debugging::Debugger *debugger;
+
+ Object *globalObject;
+
+ VM::Function *globalCode;
+
+ 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;
+
+ 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;
+
+ QVector<Function *> functions;
+
+ ExternalResourceComparison externalResourceComparison;
+
+ RegExpCache *regExpCache;
+
+ ExecutionEngine(EvalISelFactory *iselFactory = 0);
+ ~ExecutionEngine();
+
+ WithContext *newWithContext(Object *with);
+ CatchContext *newCatchContext(String* exceptionVarName, const QQmlJS::VM::Value &exceptionValue);
+ CallContext *newCallContext(FunctionObject *f, const QQmlJS::VM::Value &thisObject, QQmlJS::VM::Value *args, int argc);
+ CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QQmlJS::VM::Value &thisObject, QQmlJS::VM::Value *args, int argc);
+ ExecutionContext *pushGlobalContext();
+ void pushContext(SimpleCallContext *context);
+ ExecutionContext *popContext();
+
+ VM::Function *newFunction(const QString &name);
+
+ FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *));
+ FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function);
+ BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
+
+ Object *newObject();
+
+ String *newString(const QString &s);
+ String *newIdentifier(const QString &text);
+
+ Object *newStringObject(ExecutionContext *ctx, const Value &value);
+ Object *newNumberObject(const Value &value);
+ Object *newBooleanObject(const Value &value);
+ Object *newFunctionObject(ExecutionContext *ctx);
+
+ ArrayObject *newArrayObject(ExecutionContext *ctx);
+
+ Object *newDateObject(const Value &value);
+
+ RegExpObject *newRegExpObject(const QString &pattern, int flags);
+ RegExpObject *newRegExpObject(RegExp* re, bool global);
+
+ Object *newErrorObject(const Value &value);
+ Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message);
+ Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message);
+ Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message);
+ Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message);
+ Object *newURIErrorObject(ExecutionContext *ctx, Value message);
+
+ Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o);
+
+ void requireArgumentsAccessors(int n);
+
+ void markObjects();
+
+ Value run(VM::Function *function, ExecutionContext *ctx = 0);
+
+ void initRootContext();
+};
+
+inline void ExecutionEngine::pushContext(SimpleCallContext *context)
+{
+ context->parent = current;
+ current = context;
+}
+
+inline ExecutionContext *ExecutionEngine::popContext()
+{
+ CallContext *c = current->asCallContext();
+ if (c && !c->needsOwnArguments()) {
+ c->arguments = 0;
+ c->argumentCount = 0;
+ }
+
+ current = current->parent;
+ return current;
+}
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ENGINE_H
diff --git a/src/qml/qml/v4vm/qv4errorobject.cpp b/src/qml/qml/v4vm/qv4errorobject.cpp
new file mode 100644
index 0000000000..b69262e77c
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4errorobject.cpp
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+ErrorObject::ErrorObject(ExecutionContext *context, const Value &message, ErrorType t)
+ : Object(context->engine)
+{
+ type = Type_ErrorObject;
+ subtype = t;
+
+ if (!message.isUndefined())
+ defineDefaultProperty(context->engine->newString(QStringLiteral("message")), message);
+ defineDefaultProperty(context, QLatin1String("name"), Value::fromString(context, className()));
+}
+
+DEFINE_MANAGED_VTABLE(SyntaxErrorObject);
+
+SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+ : ErrorObject(ctx, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError)
+ , msg(message)
+{
+ vtbl = &static_vtbl;
+ prototype = ctx->engine->syntaxErrorPrototype;
+}
+
+
+
+EvalErrorObject::EvalErrorObject(ExecutionContext *ctx, const Value &message)
+ : ErrorObject(ctx, message, EvalError)
+{
+ prototype = ctx->engine->evalErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const Value &message)
+ : ErrorObject(ctx, message, RangeError)
+{
+ prototype = ctx->engine->rangeErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &message)
+ : ErrorObject(ctx, Value::fromString(ctx,message), RangeError)
+{
+ prototype = ctx->engine->rangeErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const Value &message)
+ : ErrorObject(ctx, message, ReferenceError)
+{
+ prototype = ctx->engine->referenceErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &message)
+ : ErrorObject(ctx, Value::fromString(ctx,message), ReferenceError)
+{
+ prototype = ctx->engine->referenceErrorPrototype;
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const Value &message)
+ : ErrorObject(ctx, message, TypeError)
+{
+ prototype = ctx->engine->typeErrorPrototype;
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &message)
+ : ErrorObject(ctx, Value::fromString(ctx,message), TypeError)
+{
+ prototype = ctx->engine->typeErrorPrototype;
+}
+
+URIErrorObject::URIErrorObject(ExecutionContext *ctx, const Value &message)
+ : ErrorObject(ctx, message, URIError)
+{
+ prototype = ctx->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)
+{
+ vtbl = &static_vtbl;
+}
+
+Value ErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(ctx->engine->newErrorObject(argc ? args[0] : Value::undefinedValue()));
+}
+
+Value ErrorCtor::call(Managed *that, ExecutionContext *ctx, const Value &, Value *args, int argc)
+{
+ return that->construct(ctx, args, argc);
+}
+
+Value EvalErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx, argc ? args[0] : Value::undefinedValue()));
+}
+
+Value RangeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx, argc ? args[0] : Value::undefinedValue()));
+}
+
+Value ReferenceErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx, argc ? args[0] : Value::undefinedValue()));
+}
+
+Value SyntaxErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *, int)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0));
+}
+
+Value TypeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx, argc ? args[0] : Value::undefinedValue()));
+}
+
+Value URIErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx, argc ? args[0] : Value::undefinedValue()));
+}
+
+void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj)
+{
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj));
+ ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+ obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
+ obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString()));
+}
+
+Value ErrorPrototype::method_toString(SimpleCallContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ ctx->throwTypeError();
+
+ Value name = o->get(ctx, 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, 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/v4vm/qv4errorobject.h b/src/qml/qml/v4vm/qv4errorobject.h
new file mode 100644
index 0000000000..20a20ad980
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4errorobject.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct SyntaxErrorObject;
+
+struct ErrorObject: Object {
+ enum ErrorType {
+ Error,
+ EvalError,
+ RangeError,
+ ReferenceError,
+ SyntaxError,
+ TypeError,
+ URIError
+ };
+
+ ErrorObject(ExecutionContext *context, const Value &message, ErrorType t = Error);
+
+ SyntaxErrorObject *asSyntaxError();
+};
+
+struct EvalErrorObject: ErrorObject {
+ EvalErrorObject(ExecutionContext *ctx, const Value &message);
+};
+
+struct RangeErrorObject: ErrorObject {
+ RangeErrorObject(ExecutionContext *ctx, const Value &message);
+ RangeErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct ReferenceErrorObject: ErrorObject {
+ ReferenceErrorObject(ExecutionContext *ctx, const Value &message);
+ ReferenceErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct SyntaxErrorObject: ErrorObject {
+ 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(ExecutionContext *ctx, const Value &message);
+ TypeErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct URIErrorObject: ErrorObject {
+ URIErrorObject(ExecutionContext *ctx, const Value &message);
+};
+
+struct ErrorCtor: FunctionObject
+{
+ ErrorCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct EvalErrorCtor: ErrorCtor
+{
+ EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct RangeErrorCtor: ErrorCtor
+{
+ RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ReferenceErrorCtor: ErrorCtor
+{
+ ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct SyntaxErrorCtor: ErrorCtor
+{
+ SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct TypeErrorCtor: ErrorCtor
+{
+ TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct URIErrorCtor: ErrorCtor
+{
+ URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; }
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+
+struct ErrorPrototype: ErrorObject
+{
+ // ### shouldn't be undefined
+ ErrorPrototype(ExecutionContext *context): ErrorObject(context, Value::undefinedValue()) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); }
+
+ static void init(ExecutionContext *ctx, const Value &ctor, Object *obj);
+ static Value method_toString(SimpleCallContext *ctx);
+};
+
+struct EvalErrorPrototype: EvalErrorObject
+{
+ EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct RangeErrorPrototype: RangeErrorObject
+{
+ RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct ReferenceErrorPrototype: ReferenceErrorObject
+{
+ ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct SyntaxErrorPrototype: SyntaxErrorObject
+{
+ SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct TypeErrorPrototype: TypeErrorObject
+{
+ TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct URIErrorPrototype: URIErrorObject
+{
+ URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; }
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+
+inline SyntaxErrorObject *ErrorObject::asSyntaxError()
+{
+ return subtype == SyntaxError ? static_cast<SyntaxErrorObject *>(this) : 0;
+}
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4executableallocator.cpp b/src/qml/qml/v4vm/qv4executableallocator.cpp
new file mode 100644
index 0000000000..2ad300a74b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4executableallocator.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+#include <assert.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/PageAllocation.h>
+
+using namespace QQmlJS::VM;
+
+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()
+{
+ 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;
+ }
+}
diff --git a/src/qml/qml/v4vm/qv4executableallocator.h b/src/qml/qml/v4vm/qv4executableallocator.h
new file mode 100644
index 0000000000..8e45288c54
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4executableallocator.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+#include <QMultiMap>
+#include <QHash>
+#include <QVector>
+
+namespace WTF {
+struct PageAllocation;
+};
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+class Q_AUTOTEST_EXPORT ExecutableAllocator
+{
+ struct ChunkOfPages;
+public:
+ 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(); }
+
+private:
+ struct ChunkOfPages
+ {
+ ~ChunkOfPages();
+
+ WTF::PageAllocation *pages;
+ Allocation *firstAllocation;
+
+ bool contains(Allocation *alloc) const;
+ };
+
+ QMultiMap<size_t, Allocation*> freeAllocations;
+ QMap<quintptr, ChunkOfPages*> chunks;
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4EXECUTABLEALLOCATOR_H
diff --git a/src/qml/qml/v4vm/qv4functionobject.cpp b/src/qml/qml/v4vm/qv4functionobject.cpp
new file mode 100644
index 0000000000..2380c1a994
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4functionobject.cpp
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto.h"
+#include "qv4stringobject.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+
+DEFINE_MANAGED_VTABLE(FunctionObject);
+
+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();
+}
+
+FunctionObject::FunctionObject(ExecutionContext *scope)
+ : Object(scope->engine)
+ , scope(scope)
+ , name(0)
+ , 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
+}
+
+bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value)
+{
+ FunctionObject *f = static_cast<FunctionObject *>(that);
+
+ Object *v = value.asObject();
+ if (!v)
+ return false;
+
+ Object *o = f->get(ctx, 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, ExecutionContext *context, Value *, int)
+{
+ FunctionObject *f = static_cast<FunctionObject *>(that);
+
+ Object *obj = context->engine->newObject();
+ Value proto = f->get(context, context->engine->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+ return Value::fromObject(obj);
+}
+
+Value FunctionObject::call(Managed *, ExecutionContext *, 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)
+{
+ vtbl = &static_vtbl;
+}
+
+// 15.3.2
+Value FunctionCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc)
+{
+ FunctionCtor *f = static_cast<FunctionCtor *>(that);
+ MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager);
+
+ 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;
+ Lexer lexer(engine);
+ lexer.setCode(function, 1, false);
+ Parser parser(engine);
+
+ const bool parsed = parser.parseExpression();
+
+ if (!parsed)
+ ctx->throwSyntaxError(0);
+
+ using namespace AST;
+ FunctionExpression *fe = AST::cast<FunctionExpression *>(parser.rootNode());
+ if (!fe)
+ ctx->throwSyntaxError(0);
+
+ V4IR::Module module;
+
+ Codegen cg(ctx, f->strictMode);
+ V4IR::Function *irf = cg(QString(), function, fe, &module);
+
+ QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(ctx->engine, &module));
+ VM::Function *vmf = isel->vmFunction(irf);
+
+ return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf));
+}
+
+// 15.3.1: This is equivalent to new Function(...)
+Value FunctionCtor::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc)
+{
+ return construct(that, context, args, argc);
+}
+
+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, ctx->engine->id_length).toUInt32();
+ for (quint32 i = 0; i < len; ++i) {
+ Value a = arr->getIndexed(ctx, 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(ctx, 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(ctx, 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)
+{
+ vtbl = &static_vtbl;
+ this->function = function;
+ assert(function);
+ assert(function->code);
+
+ // global function
+ if (!scope)
+ return;
+
+ MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager);
+
+ name = function->name;
+ 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, ExecutionContext *context, Value *args, int argc)
+{
+ ScriptFunction *f = static_cast<ScriptFunction *>(that);
+ assert(f->function->code);
+ Object *obj = context->engine->newObject();
+ Value proto = f->get(context, context->engine->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+
+ quintptr stackSpace[stackContextSize/sizeof(quintptr)];
+ ExecutionContext *ctx = context->engine->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, ExecutionContext *context, const Value &thisObject, Value *args, int argc)
+{
+ ScriptFunction *f = static_cast<ScriptFunction *>(that);
+ assert(f->function->code);
+ quintptr stackSpace[stackContextSize/sizeof(quintptr)];
+ ExecutionContext *ctx = context->engine->newCallContext(stackSpace, f, thisObject, args, argc);
+
+ if (!f->strictMode && !thisObject.isObject()) {
+ if (thisObject.isUndefined() || thisObject.isNull()) {
+ ctx->thisObject = Value::fromObject(context->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)
+ , code(code)
+{
+ vtbl = &static_vtbl;
+ this->name = name;
+ isBuiltinFunction = true;
+}
+
+Value BuiltinFunctionOld::construct(Managed *, ExecutionContext *ctx, Value *, int)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc)
+{
+ BuiltinFunctionOld *f = static_cast<BuiltinFunctionOld *>(that);
+ SimpleCallContext ctx;
+ ctx.type = ExecutionContext::Type_SimpleCallContext;
+ ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context?
+ ctx.marked = false;
+ ctx.thisObject = thisObject;
+ ctx.engine = f->scope->engine;
+ ctx.arguments = args;
+ ctx.argumentCount = argc;
+ context->engine->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;
+}
+
+
+DEFINE_MANAGED_VTABLE(BoundFunction);
+
+BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs)
+ : FunctionObject(scope)
+ , target(target)
+ , boundThis(boundThis)
+ , boundArgs(boundArgs)
+{
+ vtbl = &static_vtbl;
+ int len = target->get(scope, 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, ExecutionContext *context, 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(context, f->boundThis, newArgs, f->boundArgs.size() + argc);
+}
+
+Value BoundFunction::construct(Managed *that, ExecutionContext *context, 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(context, newArgs, f->boundArgs.size() + argc);
+}
+
+bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value)
+{
+ BoundFunction *f = static_cast<BoundFunction *>(that);
+ return FunctionObject::hasInstance(f->target, ctx, 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/v4vm/qv4functionobject.h b/src/qml/qml/v4vm/qv4functionobject.h
new file mode 100644
index 0000000000..4dddc048f9
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4functionobject.h
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4runtime.h"
+#include "qv4engine.h"
+#include "qv4context.h"
+#include "qv4object.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4property.h"
+#include "qv4objectiterator.h"
+#include "qv4regexp.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+#include <config.h>
+#include <assembler/MacroAssemblerCodeRef.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Value;
+struct Function;
+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 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;
+struct InternalClass;
+struct Lookup;
+
+struct Function {
+ String *name;
+
+ VM::Value (*code)(VM::ExecutionContext *, const uchar *);
+ const uchar *codeData;
+ JSC::MacroAssemblerCodeRef codeRef;
+ quint32 codeSize;
+ QByteArray unwindInfo; // CIE+FDE on x86/x86-64. Stored directly in code on ARM.
+
+ 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;
+
+ 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();
+};
+
+struct Q_V4_EXPORT FunctionObject: Object {
+ ExecutionContext *scope;
+ String *name;
+ String * const *formalParameterList;
+ String * const *varList;
+ unsigned int formalParameterCount;
+ unsigned int varCount;
+ VM::Function *function;
+
+ FunctionObject(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+ inline Value construct(ExecutionContext *context, Value *args, int argc) {
+ return vtbl->construct(this, context, args, argc);
+ }
+ inline Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) {
+ return vtbl->call(this, context, thisObject, args, argc);
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+ static void markObjects(Managed *that);
+ static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value);
+};
+
+struct FunctionCtor: FunctionObject
+{
+ FunctionCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct FunctionPrototype: FunctionObject
+{
+ FunctionPrototype(ExecutionContext *ctx): FunctionObject(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 *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct ScriptFunction: FunctionObject {
+ ScriptFunction(ExecutionContext *scope, VM::Function *function);
+
+ static Value construct(Managed *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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 *, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *);
+ static void markObjects(Managed *that);
+ static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4vm/qv4global.h b/src/qml/qml/v4vm/qv4global.h
new file mode 100644
index 0000000000..5f3f1b6d17
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4global.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_V4_LIB)
+# define Q_V4_EXPORT Q_DECL_EXPORT
+# else
+# define Q_V4_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_V4_EXPORT
+#endif
+
+namespace QQmlJS {
+namespace VM {
+
+
+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;
+ }
+};
+
+}
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GLOBAL_H
diff --git a/src/qml/qml/v4vm/qv4globalobject.cpp b/src/qml/qml/v4vm/qv4globalobject.cpp
new file mode 100644
index 0000000000..75da51a9b6
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4globalobject.cpp
@@ -0,0 +1,731 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4mm.h"
+#include "qv4value.h"
+#include "qv4context.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 QQmlJS::VM;
+
+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)
+ , qmlActivation(0)
+{
+ vtbl = &static_vtbl;
+ name = scope->engine->id_eval;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
+}
+
+EvalFunction::EvalFunction(ExecutionContext *scope, Object *qmlActivation)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+ name = scope->engine->id_eval;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
+ this->qmlActivation = qmlActivation;
+}
+
+Value EvalFunction::evalCall(ExecutionContext *parentContext, Value /*thisObject*/, Value *args, int argc, bool directCall)
+{
+ if (argc < 1)
+ return Value::undefinedValue();
+
+ 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;
+
+ QQmlJS::VM::Function *f = parseSource(ctx, QStringLiteral("eval code"),
+ code, QQmlJS::Codegen::EvalCode,
+ (directCall && parentContext->strictMode), inheritContext);
+
+ if (!f)
+ return Value::undefinedValue();
+
+ strictMode = f->isStrict || (ctx->strictMode);
+ if (qmlActivation)
+ strictMode = true;
+
+ usesArgumentsObject = f->usesArgumentsObject;
+ needsActivation = f->needsActivation();
+
+ if (strictMode) {
+ CallContext *k = ctx->engine->newCallContext(this, ctx->thisObject, 0, 0);
+ if (qmlActivation) {
+ k->activation = qmlActivation;
+ k->type = ExecutionContext::Type_QmlContext;
+ }
+ ctx = k;
+ }
+
+ // set the correct strict mode flag on the context
+ bool cstrict = ctx->strictMode;
+ ctx->strictMode = strictMode;
+
+ Value result = Value::undefinedValue();
+ try {
+ result = f->code(ctx, f->codeData);
+ } catch (Exception &ex) {
+ ctx->strictMode = cstrict;
+ if (strictMode)
+ ex.partiallyUnwindContext(parentContext);
+ throw;
+ }
+
+ ctx->strictMode = cstrict;
+
+ while (engine->current != parentContext)
+ engine->popContext();
+
+ return result;
+}
+
+
+Value EvalFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc)
+{
+ // indirect call
+ return static_cast<EvalFunction *>(that)->evalCall(context, thisObject, args, argc, false);
+}
+
+//Value EvalFunction::construct(ExecutionContext *ctx, Value *, int)
+//{
+// ctx->throwTypeError();
+// return Value::undefinedValue();
+//}
+
+QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx,
+ const QString &fileName, const QString &source,
+ QQmlJS::Codegen::Mode mode,
+ bool strictMode, bool inheritContext)
+{
+ using namespace QQmlJS;
+
+ MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager);
+
+ VM::ExecutionEngine *vm = ctx->engine;
+ V4IR::Module module;
+ VM::Function *globalCode = 0;
+
+ {
+ QQmlJS::Engine ee, *engine = &ee;
+ Lexer lexer(engine);
+ lexer.setCode(source, 1, false);
+ Parser parser(engine);
+
+ const bool parsed = parser.parseProgram();
+
+ VM::DiagnosticMessage *error = 0, **errIt = &error;
+ foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) {
+ if (m.isError()) {
+ *errIt = new VM::DiagnosticMessage;
+ (*errIt)->fileName = fileName;
+ (*errIt)->offset = m.loc.offset;
+ (*errIt)->length = m.loc.length;
+ (*errIt)->startLine = m.loc.startLine;
+ (*errIt)->startColumn = m.loc.startColumn;
+ (*errIt)->type = VM::DiagnosticMessage::Error;
+ (*errIt)->message = m.message;
+ errIt = &(*errIt)->next;
+ } else {
+ std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn
+ << ": warning: " << qPrintable(m.message) << std::endl;
+ }
+ }
+ if (error)
+ ctx->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 0;
+ }
+
+ QStringList inheritedLocals;
+ if (inheritContext)
+ for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i)
+ inheritedLocals.append(*i ? (*i)->toQString() : QString());
+
+ Codegen cg(ctx, strictMode);
+ V4IR::Function *globalIRCode = cg(fileName, source, program, &module, mode, inheritedLocals);
+ QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(vm, &module));
+ if (inheritContext)
+ isel->setUseFastLookups(false);
+ if (globalIRCode)
+ globalCode = isel->vmFunction(globalIRCode);
+ }
+
+ if (! globalCode)
+ // ### should be a syntax error
+ ctx->throwTypeError();
+ }
+
+ return globalCode;
+}
+
+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(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/v4vm/qv4globalobject.h b/src/qml/qml/v4vm/qv4globalobject.h
new file mode 100644
index 0000000000..f9776f95cb
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4globalobject.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+
+QT_END_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Q_V4_EXPORT EvalFunction : FunctionObject
+{
+ EvalFunction(ExecutionContext *scope);
+ EvalFunction(ExecutionContext *scope, Object *qmlActivation);
+
+ static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx,
+ const QString &fileName,
+ const QString &source,
+ QQmlJS::Codegen::Mode mode, bool strictMode,
+ bool inheritContext);
+
+ Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall);
+
+ using Managed::construct;
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+ Object *qmlActivation;
+
+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);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4vm/qv4identifier.h b/src/qml/qml/v4vm/qv4identifier.h
new file mode 100644
index 0000000000..1467e80986
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4identifier.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 <qv4string.h>
+#include <qv4engine.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Identifiers
+{
+ ExecutionEngine *engine;
+ uint currentIndex;
+ QHash<QString, String *> identifiers;
+public:
+
+ Identifiers(ExecutionEngine *engine) : engine(engine), currentIndex(0) {}
+
+ String *insert(const QString &s)
+ {
+ QHash<QString, String*>::const_iterator it = identifiers.find(s);
+ if (it != identifiers.constEnd())
+ return it.value();
+
+ String *str = engine->newString(s);
+ str->createHashValue();
+ if (str->subtype == String::StringType_ArrayIndex)
+ return str;
+
+ str->identifier = currentIndex;
+ if (currentIndex <= USHRT_MAX) {
+ identifiers.insert(s, str);
+ ++currentIndex;
+ }
+ return str;
+ }
+
+ void toIdentifier(String *s) {
+ if (s->identifier != UINT_MAX)
+ return;
+ if (s->subtype == String::StringType_Unknown)
+ s->createHashValue();
+ if (s->subtype == String::StringType_ArrayIndex)
+ return;
+ QHash<QString, String*>::const_iterator it = identifiers.find(s->toQString());
+ if (it != identifiers.constEnd()) {
+ s->identifier = (*it)->identifier;
+ return;
+ }
+ s->identifier = currentIndex;
+ if (currentIndex <= USHRT_MAX) {
+ identifiers.insert(s->toQString(), s);
+ ++currentIndex;
+ }
+ }
+
+ void mark() {
+ for (QHash<QString, String *>::const_iterator it = identifiers.constBegin(); it != identifiers.constEnd(); ++it)
+ (*it)->mark();
+ }
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4internalclass.cpp b/src/qml/qml/v4vm/qv4internalclass.cpp
new file mode 100644
index 0000000000..4d9bb93d0d
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4internalclass.cpp
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+#include <qv4string.h>
+#include <qv4engine.h>
+#include <qv4identifier.h>
+
+namespace QQmlJS {
+namespace VM {
+
+
+InternalClass::InternalClass(const QQmlJS::VM::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;
+
+ uint tid = string->identifier | (data.flags() << 27);
+
+ QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(tid);
+ if (tit != transitions.constEnd())
+ return tit.value();
+
+ // create a new class and add it to the tree
+ InternalClass *newClass = new InternalClass(*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->identifierCache->toIdentifier(string);
+ uint id = string->identifier | (data.flags() << 27);
+
+ assert(propertyTable.constFind(id) == propertyTable.constEnd());
+
+ QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(id);
+
+ if (index)
+ *index = size;
+ if (tit != transitions.constEnd())
+ return tit.value();
+
+ // create a new class and add it to the tree
+ InternalClass *newClass = new InternalClass(*this);
+ newClass->propertyTable.insert(string->identifier, size);
+ newClass->nameMap.append(string);
+ newClass->propertyData.append(data);
+ ++newClass->size;
+ transitions.insert(id, newClass);
+ return newClass;
+}
+
+void InternalClass::removeMember(Object *object, uint id)
+{
+ assert (propertyTable.constFind(id) != propertyTable.constEnd());
+ int propIdx = propertyTable.constFind(id).value();
+ assert(propIdx < size);
+
+ int toRemove = - (int)id;
+ QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(toRemove);
+
+ 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(toRemove, object->internalClass);
+}
+
+uint InternalClass::find(String *string)
+{
+ engine->identifierCache->toIdentifier(string);
+ uint id = string->identifier;
+
+ QHash<uint, uint>::const_iterator it = propertyTable.constFind(id);
+ if (it != propertyTable.constEnd())
+ return it.value();
+
+ 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;
+ return m_frozen;
+}
+
+
+}
+}
diff --git a/src/qml/qml/v4vm/qv4internalclass.h b/src/qml/qml/v4vm/qv4internalclass.h
new file mode 100644
index 0000000000..cc3b03190b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4internalclass.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct String;
+struct ExecutionEngine;
+struct Object;
+
+struct InternalClass {
+ ExecutionEngine *engine;
+ QHash<uint, uint> propertyTable; // id to valueIndex
+ QVector<String *> nameMap;
+
+ QVector<PropertyAttributes> propertyData;
+
+ QHash<int, InternalClass *> transitions; // id to next class, positive means add, negative delete
+
+ InternalClass *m_sealed;
+ InternalClass *m_frozen;
+
+ uint size;
+
+ InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {}
+
+ InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0);
+ InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0);
+ void removeMember(Object *object, uint id);
+ uint find(String *s);
+
+ InternalClass *sealed();
+ InternalClass *frozen();
+
+private:
+ InternalClass(const InternalClass &other);
+};
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4isel_llvm.cpp b/src/qml/qml/v4vm/qv4isel_llvm.cpp
new file mode 100644
index 0000000000..27614bae62
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_llvm.cpp
@@ -0,0 +1,1375 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4global.h"
+
+namespace QQmlJS {
+
+Q_V4_EXPORT int compileWithLLVM(IR::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(IR::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.QQmlJS::VM::Value");
+ _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::ExecutionContext")->getPointerTo();
+ _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo();
+
+ {
+ llvm::Type *args[] = { _contextPtrTy };
+ _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false);
+ }
+
+
+ foreach (IR::Function *function, module->functions)
+ (void) compileLLVMFunction(function);
+ qSwap(_fpm, fpm);
+ qSwap(_llvmModule, llvmModule);
+}
+
+void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinThrow(IR::Temp *arg)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinFinishTry()
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinPushWithScope(IR::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(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructActivationProperty(IR::Name *func,
+ IR::ExprList *args,
+ IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadThisObject(IR::Temp *temp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp)
+{
+ llvm::Value *target = getLLVMTemp(temp);
+ llvm::Value *source = CreateLoad(createValue(con));
+ CreateStore(source, target);
+}
+
+void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::setActivationProperty(IR::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(IR::Closure *closure, IR::Temp *target)
+{
+ IR::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(IR::Temp *sourceBase, const QString &sourceName, IR::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(IR::Temp *source, IR::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(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::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(IR::Temp *source, IR::Temp *targetBase, IR::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(IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ llvm::Value *t = getLLVMTemp(targetTemp);
+ llvm::Value *s = getLLVMTemp(sourceTemp);
+ CreateStore(s, t);
+}
+
+void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpNot: opName = "__qmljs_not"; break;
+ case IR::OpUMinus: opName = "__qmljs_uminus"; break;
+ case IR::OpUPlus: opName = "__qmljs_uplus"; break;
+ case IR::OpCompl: opName = "__qmljs_compl"; break;
+ case IR::OpIncrement: opName = "__qmljs_increment"; break;
+ case IR::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(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break;
+ case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break;
+ case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break;
+ case IR::OpAdd: opName = "__qmljs_llvm_add"; break;
+ case IR::OpSub: opName = "__qmljs_llvm_sub"; break;
+ case IR::OpMul: opName = "__qmljs_llvm_mul"; break;
+ case IR::OpDiv: opName = "__qmljs_llvm_div"; break;
+ case IR::OpMod: opName = "__qmljs_llvm_mod"; break;
+ case IR::OpLShift: opName = "__qmljs_llvm_shl"; break;
+ case IR::OpRShift: opName = "__qmljs_llvm_shr"; break;
+ case IR::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(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break;
+ case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break;
+ case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break;
+ case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break;
+ case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break;
+ case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break;
+ case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break;
+ case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break;
+ case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break;
+ case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break;
+ case IR::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(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break;
+ case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break;
+ case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break;
+ case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break;
+ case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break;
+ case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break;
+ case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break;
+ case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break;
+ case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break;
+ case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break;
+ case IR::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(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName)
+{
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break;
+ case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break;
+ case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break;
+ case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break;
+ case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break;
+ case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break;
+ case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break;
+ case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break;
+ case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break;
+ case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break;
+ case IR::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(IR::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(IR::Function *function)
+{
+ llvm::Function *llvmFunction = getLLVMFunction(function);
+
+ QHash<IR::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 (IR::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 (IR::BasicBlock *block, _function->basicBlocks) {
+ qSwap(_block, block);
+ SetInsertPoint(getLLVMBasicBlock(_block));
+ foreach (IR::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(IR::BasicBlock *block)
+{
+ llvm::BasicBlock *&llvmBlock = _blockMap[block];
+ if (! llvmBlock)
+ llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(),
+ _llvmFunction);
+ return llvmBlock;
+}
+
+llvm::Value *InstructionSelection::getLLVMTempReference(IR::Expr *expr)
+{
+ if (IR::Temp *t = expr->asTemp())
+ return getLLVMTemp(t);
+
+ assert(!"TODO!");
+ llvm::Value *addr = newLLVMTemp(_valueTy);
+// CreateStore(getLLVMValue(expr), addr);
+ return addr;
+}
+
+llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr)
+{
+ llvm::Value *value = 0;
+ if (IR::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(IR::Temp *temp)
+{
+ if (temp->index < 0) {
+ const int index = -temp->index -1;
+ return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"),
+ _llvmFunction->arg_begin(), getInt32(index));
+ }
+
+ return _tempMap[temp->index];
+}
+
+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(IR::Jump *s)
+{
+ CreateBr(getLLVMBasicBlock(s->target));
+}
+
+void InstructionSelection::visitCJump(IR::CJump *s)
+{
+ CreateCondBr(getLLVMCondition(s->cond),
+ getLLVMBasicBlock(s->iftrue),
+ getLLVMBasicBlock(s->iffalse));
+}
+
+void InstructionSelection::visitRet(IR::Ret *s)
+{
+ IR::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(IR::Try *)
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+#if 0
+void InstructionSelection::visitString(IR::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(IR::ExprList *exprs, int &argc)
+{
+ llvm::Value *args = 0;
+
+ argc = 0;
+ for (IR::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 (IR::ExprList *it = exprs; it; it = it->next) {
+// llvm::Value *arg = getLLVMValue(it->expr);
+// CreateStore(arg, CreateConstGEP1_32(args, i++));
+ }
+
+ return args;
+}
+
+void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ IR::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(IR::New *e, llvm::Value *result)
+{
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ IR::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(IR::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(IR::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(IR::Call *e, llvm::Value *result)
+{
+ IR::Name *base = e->base->asName();
+
+ if (! result)
+ result = newLLVMTemp(_valueTy);
+
+ if (! base->id) {
+ switch (base->builtin) {
+ case IR::Name::builtin_invalid:
+ break;
+
+ case IR::Name::builtin_typeof:
+ CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"),
+ _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case IR::Name::builtin_throw:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"),
+ _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr));
+ _llvmValue = llvm::UndefValue::get(_valueTy);
+ return;
+
+ case IR::Name::builtin_finish_try:
+ // ### FIXME.
+ return;
+
+ case IR::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 IR::Name::builtin_foreach_next_property_name:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"),
+ result, getLLVMTempReference(e->args->expr));
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case IR::Name::builtin_delete: {
+ if (IR::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 (IR::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 (IR::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(IR::New *e, llvm::Value *result)
+{
+ IR::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(IR::Call *e)
+{
+ if (e->base->asMember()) {
+ genCallMember(e);
+ } else if (e->base->asTemp()) {
+ genCallTemp(e);
+ } else if (e->base->asName()) {
+ genCallName(e);
+ } else if (IR::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(IR::New *e)
+{
+ if (e->base->asMember()) {
+ genConstructMember(e);
+ } else if (e->base->asTemp()) {
+ genConstructTemp(e);
+ } else if (e->base->asName()) {
+ genConstructName(e);
+ } else if (IR::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(IR::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(IR::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(IR::Const *e)
+{
+ llvm::Value *tmp = newLLVMTemp(_valueTy);
+
+ switch (e->type) {
+ case IR::UndefinedType:
+ CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp);
+ break;
+
+ case IR::NullType:
+ CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp);
+ break;
+
+ case IR::BoolType:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp,
+ getInt1(e->value ? 1 : 0));
+ break;
+
+ case IR::NumberType:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp,
+ llvm::ConstantFP::get(_numberTy, e->value));
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ }
+
+ return tmp;
+}
+
+llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e)
+{
+ if (IR::Temp *t = e->asTemp()) {
+ return getLLVMTemp(t);
+ } else if (IR::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/v4vm/qv4isel_llvm_p.h b/src/qml/qml/v4vm/qv4isel_llvm_p.h
new file mode 100644
index 0000000000..00b6527e6a
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_llvm_p.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 IR::InstructionSelection
+{
+public:
+ InstructionSelection(llvm::LLVMContext &context);
+
+ void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm);
+
+public: // methods from InstructionSelection:
+ virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result);
+ virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result);
+ virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result);
+ virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result);
+ virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result);
+ virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result);
+ virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result);
+ virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result);
+ virtual void callBuiltinDeleteValue(IR::Temp *result);
+ virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result);
+ virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result);
+ virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result);
+ virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result);
+ virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result);
+ virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result);
+ virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result);
+ virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result);
+ virtual void callBuiltinThrow(IR::Temp *arg);
+ virtual void callBuiltinCreateExceptionHandler(IR::Temp *result);
+ virtual void callBuiltinFinishTry();
+ virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result);
+ virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result);
+ virtual void callBuiltinPushWithScope(IR::Temp *arg);
+ virtual void callBuiltinPopScope();
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name);
+ virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter);
+ virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value);
+ virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args);
+ virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result);
+ virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result);
+ virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result);
+ virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result);
+ virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result);
+ virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result);
+ virtual void loadThisObject(IR::Temp *temp);
+ virtual void loadConst(IR::Const *con, IR::Temp *temp);
+ virtual void loadString(const QString &str, IR::Temp *targetTemp);
+ virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp);
+ virtual void getActivationProperty(const QString &name, IR::Temp *temp);
+ virtual void setActivationProperty(IR::Temp *source, const QString &targetName);
+ virtual void initClosure(IR::Closure *closure, IR::Temp *target);
+ virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target);
+ virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target);
+ virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex);
+ virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp);
+ virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp);
+ virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target);
+ virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName);
+ virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName);
+
+public: // visitor methods for StmtVisitor:
+ virtual void visitJump(IR::Jump *);
+ virtual void visitCJump(IR::CJump *);
+ virtual void visitRet(IR::Ret *);
+ virtual void visitTry(IR::Try *);
+
+private:
+ llvm::Function *getRuntimeFunction(llvm::StringRef str);
+ llvm::Function *getLLVMFunction(IR::Function *function);
+ llvm::Function *compileLLVMFunction(IR::Function *function);
+ llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block);
+ llvm::Value *getLLVMTempReference(IR::Expr *expr);
+ llvm::Value *getLLVMCondition(IR::Expr *expr);
+ llvm::Value *getLLVMTemp(IR::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(IR::ExprList *args, int &argc);
+ void genCallTemp(IR::Call *e, llvm::Value *result = 0);
+ void genCallName(IR::Call *e, llvm::Value *result = 0);
+ void genCallMember(IR::Call *e, llvm::Value *result = 0);
+ void genConstructTemp(IR::New *e, llvm::Value *result = 0);
+ void genConstructName(IR::New *e, llvm::Value *result = 0);
+ void genConstructMember(IR::New *e, llvm::Value *result = 0);
+ llvm::Value *createValue(IR::Const *e);
+ llvm::Value *toValuePtr(IR::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;
+ IR::Function *_function;
+ IR::BasicBlock *_block;
+ QHash<IR::Function *, llvm::Function *> _functionMap;
+ QHash<IR::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/v4vm/qv4isel_masm.cpp b/src/qml/qml/v4vm/qv4isel_masm.cpp
new file mode 100644
index 0000000000..a9d41ca32e
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_masm.cpp
@@ -0,0 +1,1252 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4object.h"
+#include "qv4functionobject.h"
+#include "qv4regexpobject.h"
+#include "qv4unwindhelper.h"
+#include "qv4lookup.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 QQmlJS::VM;
+
+/* 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, VM::Function *vmFunction, VM::ExecutionEngine *engine)
+ : _function(function), _vmFunction(vmFunction), _engine(engine)
+{
+}
+
+void Assembler::registerBlock(V4IR::BasicBlock* block)
+{
+ _addrs[block] = label();
+}
+
+void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target)
+{
+ if (current->index + 1 != target->index)
+ _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;
+ VM::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;
+ }
+ }
+ if (t->index < 0) {
+ const int arg = -t->index - 1;
+ loadPtr(Address(context, offsetof(CallContext, arguments)), reg);
+ offset = arg * sizeof(Value);
+ } else if (t->index < f->locals.size()) {
+ loadPtr(Address(context, offsetof(CallContext, locals)), reg);
+ offset = t->index * sizeof(Value);
+ } else {
+ assert(t->scope == 0);
+ const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1;
+ offset = - sizeof(Value) * (arg + 1);
+ offset -= sizeof(void*) * calleeSavedRegisterCount;
+ reg = LocalsRegister;
+ }
+ 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.
+ loadArgument(source, ReturnValueRegister);
+ storeArgument(ReturnValueRegister, 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.
+ loadArgument(source, ReturnValueRegister);
+ storeArgument(ReturnValueRegister, result);
+#else
+ if (V4IR::Temp *temp = source->asTemp()) {
+ loadDouble(temp, FPGpr0);
+ storeDouble(FPGpr0, result);
+ } else if (V4IR::Const *c = source->asConst()) {
+ VM::Value v = convertToValue(c);
+ storeValue(v, result);
+ } else {
+ assert(! "not implemented");
+ }
+#endif
+}
+
+
+void Assembler::storeValue(VM::Value value, V4IR::Temp* destination)
+{
+ Address addr = loadTempAddress(ScratchRegister, destination);
+ storeValue(value, addr);
+}
+
+void Assembler::enterStandardStackFrame(int locals)
+{
+ platformEnterStandardStackFrame();
+
+ // ### FIXME: Handle through calleeSavedRegisters mechanism
+ // or eliminate StackFrameRegister altogether.
+ push(StackFrameRegister);
+ move(StackPointerRegister, StackFrameRegister);
+
+ // space for the locals and callee saved registers
+ int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount;
+
+#if CPU(X86) || CPU(X86_64)
+ frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX
+#endif
+ 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]);
+
+ // space for the locals and the callee saved registers
+ int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount;
+#if CPU(X86) || CPU(X86_64)
+ frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX
+#endif
+ // 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), Assembler::ScratchRegister);
+ add32(Assembler::ScratchRegister, 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(VM::Value, tag);
+ leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type));
+ }
+
+ Jump rightTypeCheck;
+ if (right->asTemp()) {
+ Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp());
+ typeAddress.offset += offsetof(VM::Value, tag);
+ rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type));
+ }
+
+ if (left->asTemp()) {
+ Address leftValue = loadTempAddress(ScratchRegister, left->asTemp());
+ leftValue.offset += offsetof(VM::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(VM::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(VM::Value, int_32);
+ store32(IntegerOpRegister, resultValueAddr);
+
+ Address resultTypeAddr = resultAddr;
+ resultTypeAddr.offset += offsetof(VM::Value, tag);
+ store32(TrustedImm32(VM::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)
+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::link(VM::Function *vmFunc)
+{
+ Label endOfCode = label();
+#ifdef Q_PROCESSOR_ARM
+ // 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);
+
+ 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));
+ }
+ }
+
+#ifdef Q_PROCESSOR_ARM
+ UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode));
+#endif
+
+ static bool showCode = !qgetenv("SHOW_CODE").isNull();
+ if (showCode) {
+#if OS(LINUX)
+ char* disasmOutput = 0;
+ size_t disasmLength = 0;
+ FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength);
+ 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)
+ fclose(disasmStream);
+#if CPU(X86) || CPU(X86_64)
+ QHash<void*, String*> idents;
+ printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers);
+#endif
+ free(disasmOutput);
+#endif
+ } else {
+ vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ }
+
+ vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress();
+}
+
+InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module)
+ : EvalInstructionSelection(engine, module)
+ , _block(0)
+ , _function(0)
+ , _vmFunction(0)
+ , _as(0)
+{
+}
+
+InstructionSelection::~InstructionSelection()
+{
+ delete _as;
+}
+
+void InstructionSelection::run(VM::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());
+
+ int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1;
+ locals = (locals + 1) & ~1;
+ _as->enterStandardStackFrame(locals);
+
+ int contextPointer = 0;
+#ifndef VALUE_FITS_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
+
+ foreach (V4IR::BasicBlock *block, _function->basicBlocks) {
+ _block = block;
+ _as->registerBlock(_block);
+
+ 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) {
+ s->accept(this);
+ }
+ }
+
+ _as->leaveStandardStackFrame(locals);
+#ifndef ARGUMENTS_IN_REGISTERS
+ // 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();
+
+ _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);
+ delete _as;
+ _as = oldAssembler;
+}
+
+void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ VM::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_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg));
+}
+
+typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr));
+static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody,
+ VM::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), Assembler::ContextRegister);
+}
+
+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::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->loadPtr(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister);
+ _as->storeArgument(Assembler::ReturnValueRegister, 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);
+ generateFunctionCall(Assembler::Void, __qmljs_get_global_lookup, Assembler::ContextRegister, Assembler::PointerToValue(temp),
+ Assembler::TrustedImm32(index));
+ 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)
+{
+ VM::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) {
+ VM::String *s = identifier(name);
+ uint index = addLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_get_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(target),
+ Assembler::Reference(base), Assembler::TrustedImm32(index));
+ } 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) {
+ VM::String *s = identifier(targetName);
+ uint index = addLookup(s);
+ generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup,
+ Assembler::ContextRegister, Assembler::Reference(targetBase),
+ Assembler::TrustedImm32(index), 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)
+{
+ VM::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::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target)
+{
+ _as->generateBinOp(oper, target, leftSource, rightSource);
+}
+
+void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName)
+{
+ VM::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)
+{
+ VM::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)
+{
+ VM::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);
+ VM::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));
+}
+
+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);
+ VM::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(VM::Value, tag);
+ Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type));
+
+ Address data = temp;
+ data.offset += offsetof(VM::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()) {
+ VM::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(ARGUMENTS_IN_REGISTERS) && defined(VALUE_FITS_IN_REGISTER)
+ _as->copyValue(Assembler::ReturnValueRegister, t);
+#else
+ _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister);
+ _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t);
+#endif
+ return;
+ }
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(s);
+}
+
+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(VM::String *name)
+{
+ uint index = (uint)_lookups.size();
+ VM::Lookup l;
+ l.lookupProperty = Lookup::lookupPropertyGeneric;
+ 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(VM::String *name)
+{
+ uint index = (uint)_lookups.size();
+ VM::Lookup l;
+ l.lookupGlobal = Lookup::lookupGlobalGeneric;
+ 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/v4vm/qv4isel_masm_p.h b/src/qml/qml/v4vm/qv4isel_masm_p.h
new file mode 100644
index 0000000000..9f8704725f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_masm_p.h
@@ -0,0 +1,894 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4isel_util_p.h"
+#include "qv4object.h"
+#include "qv4runtime.h"
+
+#include <QtCore/QHash>
+#include <config.h>
+#include <wtf/Vector.h>
+#include <assembler/MacroAssembler.h>
+
+namespace QQmlJS {
+namespace MASM {
+
+class Assembler : public JSC::MacroAssembler
+{
+public:
+ Assembler(V4IR::Function* function, VM::Function *vmFunction, VM::ExecutionEngine *engine);
+#if CPU(X86)
+
+#undef VALUE_FITS_IN_REGISTER
+#undef ARGUMENTS_IN_REGISTERS
+#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;
+ }
+
+ inline void platformEnterStandardStackFrame() {}
+ inline void platformLeaveStandardStackFrame() {}
+#elif CPU(X86_64)
+
+#define VALUE_FITS_IN_REGISTER
+#define ARGUMENTS_IN_REGISTERS
+#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;
+
+ 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];
+ };
+ inline void platformEnterStandardStackFrame() {}
+ inline void platformLeaveStandardStackFrame() {}
+#elif CPU(ARM)
+
+#undef VALUE_FITS_IN_REGISTER
+#define ARGUMENTS_IN_REGISTERS
+#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);
+ };
+ 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);
+ pop(JSC::ARMRegisters::r0);
+ pop(JSC::ARMRegisters::r1);
+ pop(JSC::ARMRegisters::r2);
+ pop(JSC::ARMRegisters::r3);
+ }
+#else
+#error Argh.
+#endif
+ static const int calleeSavedRegisterCount;
+
+ // 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 registerBlock(V4IR::BasicBlock*);
+ 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 loadArgument(RegisterID source, RegisterID dest)
+ {
+ move(source, dest);
+ }
+
+ void loadArgument(TrustedImmPtr ptr, RegisterID dest)
+ {
+ move(TrustedImmPtr(ptr), dest);
+ }
+
+ void loadArgument(const Pointer& ptr, RegisterID dest)
+ {
+ addPtr(TrustedImm32(ptr.offset), ptr.base, dest);
+ }
+
+ void loadArgument(PointerToValue temp, RegisterID dest)
+ {
+ if (!temp.value) {
+ loadArgument(TrustedImmPtr(0), dest);
+ } else {
+ Pointer addr = loadTempAddress(dest, temp.value);
+ loadArgument(addr, dest);
+ }
+ }
+
+ void loadArgument(Reference temp, RegisterID dest)
+ {
+ assert(temp.value);
+ Pointer addr = loadTempAddress(dest, temp.value);
+ loadArgument(addr, dest);
+ }
+
+ void loadArgument(ReentryBlock block, RegisterID dest)
+ {
+ assert(block.block);
+ DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest);
+ addPatch(patch, block.block);
+ }
+
+#ifdef VALUE_FITS_IN_REGISTER
+ void loadArgument(V4IR::Temp* temp, RegisterID dest)
+ {
+ if (!temp) {
+ VM::Value undefined = VM::Value::undefinedValue();
+ move(TrustedImm64(undefined.val), dest);
+ } else {
+ Pointer addr = loadTempAddress(dest, temp);
+ load64(addr, dest);
+ }
+ }
+
+ void loadArgument(V4IR::Const* c, RegisterID dest)
+ {
+ VM::Value v = convertToValue(c);
+ move(TrustedImm64(v.val), dest);
+ }
+
+ void loadArgument(V4IR::Expr* expr, RegisterID dest)
+ {
+ if (!expr) {
+ VM::Value undefined = VM::Value::undefinedValue();
+ move(TrustedImm64(undefined.val), dest);
+ } else if (expr->asTemp()){
+ loadArgument(expr->asTemp(), dest);
+ } else if (expr->asConst()) {
+ loadArgument(expr->asConst(), dest);
+ } else {
+ assert(!"unimplemented expression type in loadArgument");
+ }
+ }
+#else
+ void loadArgument(V4IR::Expr*, RegisterID)
+ {
+ assert(!"unimplemented: expression in loadArgument");
+ }
+#endif
+
+ void loadArgument(VM::String* string, RegisterID dest)
+ {
+ loadArgument(TrustedImmPtr(string), dest);
+ }
+
+ void loadArgument(TrustedImm32 imm32, RegisterID dest)
+ {
+ xorPtr(dest, dest);
+ if (imm32.m_value)
+ move(imm32, dest);
+ }
+
+ void storeArgument(RegisterID src, V4IR::Temp *temp)
+ {
+ if (temp) {
+ Pointer addr = loadTempAddress(ScratchRegister, temp);
+#ifdef VALUE_FITS_IN_REGISTER
+ store64(src, addr);
+#else
+ // If the value doesn't fit into a register, then the
+ // register contains the address to where the argument
+ // (return value) is stored. Copy it from there.
+ copyValue(addr, Pointer(src, 0));
+#endif
+ }
+ }
+
+#ifdef VALUE_FITS_IN_REGISTER
+ void storeArgument(RegisterID src, const Pointer &dest)
+ {
+ store64(src, dest);
+ }
+#endif
+
+ void storeArgument(RegisterID src, RegisterID dest)
+ {
+ move(src, dest);
+ }
+
+ void storeArgument(RegisterID, VoidType)
+ {
+ }
+
+ using JSC::MacroAssembler::push;
+
+ void push(const Pointer& ptr)
+ {
+ addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister);
+ push(ScratchRegister);
+ }
+
+ void push(VM::Value value)
+ {
+#ifdef VALUE_FITS_IN_REGISTER
+ move(TrustedImm64(value.val), ScratchRegister);
+ push(ScratchRegister);
+#else
+ move(TrustedImm32(value.tag), ScratchRegister);
+ push(ScratchRegister);
+ move(TrustedImm32(value.int_32), ScratchRegister);
+ push(ScratchRegister);
+#endif
+ }
+
+ void push(PointerToValue temp)
+ {
+ if (temp.value) {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp.value);
+ push(ptr);
+ } else {
+ push(TrustedImmPtr(0));
+ }
+ }
+
+ void push(Reference temp)
+ {
+ assert (temp.value);
+
+ Pointer ptr = loadTempAddress(ScratchRegister, temp.value);
+ push(ptr);
+ }
+
+ void push(ReentryBlock block)
+ {
+ assert(block.block);
+ DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister);
+ push(ScratchRegister);
+ addPatch(patch, block.block);
+ }
+
+ void push(V4IR::Temp* temp)
+ {
+ if (temp) {
+ Address addr = loadTempAddress(ScratchRegister, temp);
+ addr.offset += 4;
+ push(addr);
+ addr.offset -= 4;
+ push(addr);
+ } else {
+ VM::Value undefined = VM::Value::undefinedValue();
+ push(undefined);
+ }
+ }
+
+ void push(V4IR::Const* c)
+ {
+ VM::Value v = convertToValue(c);
+ push(v);
+ }
+
+ void push(V4IR::Expr* e)
+ {
+ if (!e) {
+ VM::Value undefined = VM::Value::undefinedValue();
+ push(undefined);
+ } else if (V4IR::Const *c = e->asConst())
+ push(c);
+ else if (V4IR::Temp *t = e->asTemp()) {
+ push(t);
+ } else {
+ assert(!"Trying to push an expression that is not a Temp or Const");
+ }
+ }
+
+ void push(TrustedImmPtr ptr)
+ {
+ move(TrustedImmPtr(ptr), ScratchRegister);
+ push(ScratchRegister);
+ }
+
+ void push(VM::String* name)
+ {
+ push(TrustedImmPtr(name));
+ }
+
+ 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(VM::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(VM::Value value, V4IR::Temp* temp);
+
+ void enterStandardStackFrame(int locals);
+ void leaveStandardStackFrame(int locals);
+
+ static inline int sizeOfArgument(VoidType)
+ { return 0; }
+ static inline int sizeOfArgument(RegisterID)
+ { return RegisterSize; }
+ static inline int sizeOfArgument(V4IR::Temp*)
+ { return 8; } // Size of value
+ static inline int sizeOfArgument(V4IR::Expr*)
+ { return 8; } // Size of value
+ static inline int sizeOfArgument(const Pointer&)
+ { return sizeof(void*); }
+ static inline int sizeOfArgument(VM::String*)
+ { return sizeof(VM::String*); }
+ static inline int sizeOfArgument(const PointerToValue &)
+ { return sizeof(void *); }
+ static inline int sizeOfArgument(const Reference &)
+ { return sizeof(void *); }
+ static inline int sizeOfArgument(const ReentryBlock &)
+ { return sizeof(void *); }
+ static inline int sizeOfArgument(TrustedImmPtr)
+ { return sizeof(void*); }
+ static inline int sizeOfArgument(TrustedImm32)
+ { return 4; }
+
+ template <int argumentNumber, typename T>
+ int loadArgumentOnStackOrRegister(const T &value)
+ {
+ if (argumentNumber < RegisterArgumentCount) {
+ loadArgument(value, registerForArgument(argumentNumber));
+ return 0;
+ } else {
+ push(value);
+ return sizeOfArgument(value);
+ }
+ }
+
+ template <int argumentNumber>
+ int loadArgumentOnStackOrRegister(const VoidType &value)
+ {
+ Q_UNUSED(value);
+ return 0;
+ }
+
+ template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6)
+ {
+ int totalNumberOfArgs = 6;
+
+ // If necessary reserve space for the return value on the stack and
+ // pass the pointer to it as the first hidden parameter.
+ bool returnValueOnStack = false;
+ int sizeOfReturnValueOnStack = sizeOfArgument(r);
+ if (sizeOfReturnValueOnStack > RegisterSize) {
+ sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister);
+ ++totalNumberOfArgs;
+ returnValueOnStack = true;
+ }
+
+ int stackSpaceUsedForArgs = 0;
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<5>(arg6);
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<4>(arg5);
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<3>(arg4);
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<2>(arg3);
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<1>(arg2);
+ stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<0>(arg1);
+
+ if (returnValueOnStack) {
+ // Load address of return value
+ push(Pointer(StackPointerRegister, stackSpaceUsedForArgs));
+ }
+
+ callAbsolute(functionName, function);
+
+ int stackSizeToCorrect = stackSpaceUsedForArgs;
+ if (returnValueOnStack)
+ stackSizeToCorrect += sizeOfReturnValueOnStack;
+
+ storeArgument(ReturnValueRegister, r);
+
+ if (stackSizeToCorrect)
+ add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister);
+ }
+
+ template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr 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 Arg1, typename Arg2, typename Arg3, typename Arg4>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType());
+ }
+
+ template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType());
+ }
+
+ template <typename ArgRet, typename Arg1, typename Arg2>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2)
+ {
+ generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType());
+ }
+
+ template <typename ArgRet, typename Arg1>
+ void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr 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;
+ VM::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(VM::Function *vmFunc);
+
+private:
+ V4IR::Function *_function;
+ VM::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;
+
+ VM::ExecutionEngine *_engine;
+};
+
+class Q_V4_EXPORT InstructionSelection:
+ protected V4IR::InstructionSelection,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(VM::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 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 callValue(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::Temp *leftSource, V4IR::Temp *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(VM::Value) * (-index)
+ - sizeof(void*) // size of ebp
+ - sizeof(void*) * Assembler::calleeSavedRegisterCount
+ );
+ }
+ Pointer baseAddressForCallArguments()
+ {
+ return argumentAddressForCall(0);
+ }
+
+ VM::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)(VM::ExecutionContext *, VM::Value *result, VM::String *name, VM::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(VM::String *name);
+ uint addGlobalLookup(VM::String *name);
+
+ V4IR::BasicBlock *_block;
+ V4IR::Function* _function;
+ VM::Function* _vmFunction;
+ QVector<VM::Lookup> _lookups;
+ Assembler* _as;
+ QSet<V4IR::BasicBlock*> _reentryBlocks;
+};
+
+class Q_V4_EXPORT ISelFactory: public EvalISelFactory
+{
+public:
+ virtual ~ISelFactory() {}
+ virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module)
+ { return new InstructionSelection(engine, module); }
+};
+
+} // end of namespace MASM
+} // end of namespace QQmlJS
+
+#endif // QV4ISEL_MASM_P_H
diff --git a/src/qml/qml/v4vm/qv4isel_p.cpp b/src/qml/qml/v4vm/qv4isel_p.cpp
new file mode 100644
index 0000000000..417e38550e
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_p.cpp
@@ -0,0 +1,398 @@
+#include "debugging.h"
+#include "qv4engine.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4isel_util_p.h"
+#include "qv4functionobject.h"
+
+#include <QString>
+
+#include <cassert>
+
+namespace {
+QTextStream qout(stderr, QIODevice::WriteOnly);
+} // anonymous namespace
+
+using namespace QQmlJS;
+using namespace QQmlJS::V4IR;
+
+EvalInstructionSelection::EvalInstructionSelection(VM::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()
+{}
+
+VM::Function *EvalInstructionSelection::createFunctionMapping(VM::Function *outer, Function *irFunction)
+{
+ VM::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;
+
+ 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);
+
+ if (_engine->debugger)
+ _engine->debugger->mapFunction(vmFunction, irFunction);
+
+ return vmFunction;
+}
+
+VM::Function *EvalInstructionSelection::vmFunction(Function *f) {
+ VM::Function *function = _irToVM[f];
+ if (!function->code)
+ run(function, f);
+ return function;
+}
+
+void InstructionSelection::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()) {
+ if (b->left->asTemp() && b->right->asTemp()) {
+ binop(b->op, b->left->asTemp(), b->right->asTemp(), 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()) {
+ callProperty(member->base, *member->name, c->args, t);
+ return;
+ } else if (Subscript *s = c->base->asSubscript()) {
+ callSubscript(s->base, s->index, c->args, t);
+ return;
+ } else if (V4IR::Temp *value = c->base->asTemp()) {
+ callValue(value, c->args, 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");
+}
+
+InstructionSelection::~InstructionSelection()
+{
+}
+
+void InstructionSelection::visitEnter(Enter *)
+{
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::visitLeave(Leave *)
+{
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::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()) {
+ callProperty(member->base, *member->name, c->args, 0);
+ } else if (Subscript *s = c->base->asSubscript()) {
+ callSubscript(s->base, s->index, c->args, 0);
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+ } else {
+ Q_UNIMPLEMENTED();
+ }
+}
+
+void InstructionSelection::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;
+
+ default:
+ break;
+ }
+
+ Q_UNIMPLEMENTED();
+ call->dump(qout); qout << endl;
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
diff --git a/src/qml/qml/v4vm/qv4isel_p.h b/src/qml/qml/v4vm/qv4isel_p.h
new file mode 100644
index 0000000000..e85127c5a9
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+
+#include <qglobal.h>
+#include <QHash>
+
+namespace QQmlJS {
+
+namespace VM {
+struct ExecutionEngine;
+struct Function;
+} // namespace VM
+
+class Q_V4_EXPORT EvalInstructionSelection
+{
+public:
+ EvalInstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module);
+ virtual ~EvalInstructionSelection() = 0;
+
+ VM::Function *vmFunction(V4IR::Function *f);
+
+ void setUseFastLookups(bool b) { useFastLookups = b; }
+
+protected:
+ VM::Function *createFunctionMapping(VM::Function *outer, V4IR::Function *irFunction);
+ VM::ExecutionEngine *engine() const { return _engine; }
+ virtual void run(VM::Function *vmFunction, V4IR::Function *function) = 0;
+
+private:
+ VM::ExecutionEngine *_engine;
+ QHash<V4IR::Function *, VM::Function *> _irToVM;
+protected:
+ bool useFastLookups;
+};
+
+class Q_V4_EXPORT EvalISelFactory
+{
+public:
+ virtual ~EvalISelFactory() = 0;
+ virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module) = 0;
+};
+
+namespace V4IR {
+class Q_V4_EXPORT InstructionSelection: protected V4IR::StmtVisitor
+{
+public:
+ virtual ~InstructionSelection() = 0;
+
+public: // visitor methods for StmtVisitor:
+ virtual void visitMove(V4IR::Move *s);
+ virtual void visitEnter(V4IR::Enter *);
+ virtual void visitLeave(V4IR::Leave *);
+ 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 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 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::Temp *leftSource, V4IR::Temp *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
+
+#endif // QV4ISEL_P_H
diff --git a/src/qml/qml/v4vm/qv4isel_util_p.h b/src/qml/qml/v4vm/qv4isel_util_p.h
new file mode 100644
index 0000000000..e10a9658f2
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4isel_util_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+
+namespace QQmlJS {
+
+inline VM::Value convertToValue(V4IR::Const *c)
+{
+ switch (c->type) {
+ case V4IR::MissingType:
+ return VM::Value::deletedValue();
+ case V4IR::NullType:
+ return VM::Value::nullValue();
+ case V4IR::UndefinedType:
+ return VM::Value::undefinedValue();
+ case V4IR::BoolType:
+ return VM::Value::fromBoolean(c->value != 0);
+ 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 VM::Value::fromInt32(ival);
+ } else {
+ return VM::Value::fromDouble(c->value);
+ }
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+} // namespace QQmlJS
+
+#endif // QV4ISEL_UTIL_P_H
diff --git a/src/qml/qml/v4vm/qv4jsir.cpp b/src/qml/qml/v4vm/qv4jsir.cpp
new file mode 100644
index 0000000000..3dad094624
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4jsir.cpp
@@ -0,0 +1,948 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 {
+
+const char *typeName(Type t)
+{
+ switch (t) {
+ case UndefinedType: return "undefined";
+ case NullType: return "null";
+ case BoolType: return "bool";
+ case NumberType: return "number";
+ default: return "invalid";
+ }
+}
+
+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 visitEnter(Enter *s)
+ {
+ s->expr = cleanup(s->expr);
+ }
+
+ virtual void visitLeave(Leave *)
+ {
+ // nothing to do for Leave statements
+ }
+
+ 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
+ }
+
+ // 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 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);
+ }
+};
+
+void Const::dump(QTextStream &out)
+{
+ 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;
+ }
+}
+
+void String::dump(QTextStream &out)
+{
+ 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)
+{
+ 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";
+ }
+ return "builtin_(###FIXME)";
+};
+
+
+void Name::dump(QTextStream &out)
+{
+ if (id)
+ out << *id;
+ else
+ out << builtin_to_string(builtin);
+}
+
+void Temp::dump(QTextStream &out)
+{
+ if (index < 0) {
+ out << '#' << -(index + 1); // negative and 1-based.
+ } else {
+ out << '%' << index; // temp
+ }
+ if (scope)
+ out << "@" << scope;
+}
+
+void Closure::dump(QTextStream &out)
+{
+ QString name = value->name ? *value->name : QString();
+ if (name.isEmpty())
+ name.sprintf("%p", value);
+ out << "closure(" << name << ')';
+}
+
+void Unop::dump(QTextStream &out)
+{
+ out << opname(op);
+ expr->dump(out);
+}
+
+void Binop::dump(QTextStream &out)
+{
+ left->dump(out);
+ out << ' ' << opname(op) << ' ';
+ right->dump(out);
+}
+
+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 << ')';
+}
+
+void New::dump(QTextStream &out)
+{
+ 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)
+{
+ base->dump(out);
+ out << '[';
+ index->dump(out);
+ out << ']';
+}
+
+void Member::dump(QTextStream &out)
+{
+ base->dump(out);
+ out << '.' << *name;
+}
+
+void Exp::dump(QTextStream &out, Mode)
+{
+ out << "(void) ";
+ expr->dump(out);
+ out << ';';
+}
+
+void Enter::dump(QTextStream &out, Mode)
+{
+ out << "%enter(";
+ expr->dump(out);
+ out << ");";
+}
+
+void Leave::dump(QTextStream &out, Mode)
+{
+ out << "%leave";
+ 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 << ';';
+}
+
+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(BasicBlockInsertMode mode)
+{
+ BasicBlock *block = new BasicBlock(this);
+ 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(int index, uint scope)
+{
+ Temp *e = function->New<Temp>();
+ e->init(index, scope);
+ return e;
+}
+
+Expr *BasicBlock::CONST(Type type, double value)
+{
+ Const *e = function->New<Const>();
+ 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::UNOP(AluOp op, Temp *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(Temp *base, Temp *index)
+{
+ Subscript *e = function->New<Subscript>();
+ e->init(base, index);
+ return e;
+}
+
+Expr *BasicBlock::MEMBER(Temp *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);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::ENTER(Expr *expr)
+{
+ if (isTerminated())
+ return 0;
+
+ Enter *s = function->New<Enter>();
+ s->init(expr);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::LEAVE()
+{
+ if (isTerminated())
+ return 0;
+
+ Leave *s = function->New<Leave>();
+ s->init();
+ statements.append(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);
+ statements.append(s);
+ return s;
+}
+
+Stmt *BasicBlock::JUMP(BasicBlock *target)
+{
+ if (isTerminated())
+ return 0;
+
+ Jump *s = function->New<Jump>();
+ s->init(target);
+ statements.append(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);
+ statements.append(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);
+ statements.append(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);
+ statements.append(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);
+ out << endl;
+ }
+}
+
+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)
+{
+ cloned = block->TEMP(e->index, e->scope);
+}
+
+void CloneExpr::visitClosure(Closure *e)
+{
+ cloned = block->CLOSURE(e->value);
+}
+
+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/v4vm/qv4jsir_p.h b/src/qml/qml/v4vm/qv4jsir_p.h
new file mode 100644
index 0000000000..47368449fc
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4jsir_p.h
@@ -0,0 +1,821 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "qv4global.h"
+#include <private/qqmljsmemorypool_p.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QString>
+#include <QtCore/QBitArray>
+
+#ifdef CONST
+#undef CONST
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+class QQmlType;
+
+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 VM {
+struct ExecutionContext;
+struct Value;
+}
+
+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 Unop;
+struct Binop;
+struct Call;
+struct New;
+struct Subscript;
+struct Member;
+
+// statements
+struct Exp;
+struct Enter;
+struct Leave;
+struct Move;
+struct Jump;
+struct CJump;
+struct Ret;
+struct Try;
+
+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 {
+ MissingType, // Used to indicate holes in array initialization (e.g. [,,])
+ UndefinedType,
+ NullType,
+ BoolType,
+ NumberType
+};
+
+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 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 visitEnter(Enter *) = 0;
+ virtual void visitLeave(Leave *) = 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;
+};
+
+struct Expr {
+ 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 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) = 0;
+};
+
+struct ExprList {
+ Expr *expr;
+ ExprList *next;
+
+ void init(Expr *expr, ExprList *next = 0)
+ {
+ this->expr = expr;
+ this->next = next;
+ }
+};
+
+struct Const: Expr {
+ Type type;
+ 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 {
+ 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);
+ static QString escape(const QString &s);
+};
+
+struct RegExp: Expr {
+ // needs to be compatible with the flags in the lexer
+ 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);
+};
+
+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
+ };
+
+ 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);
+};
+
+struct Temp: Expr {
+ int index;
+ int scope; // how many scopes outside the current one?
+
+ void init(int index, int scope = 0)
+ {
+ 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);
+};
+
+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);
+};
+
+struct Unop: Expr {
+ AluOp op;
+ Temp *expr;
+
+ void init(AluOp op, Temp *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);
+};
+
+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);
+};
+
+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);
+};
+
+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);
+};
+
+struct Subscript: Expr {
+ Temp *base;
+ Temp *index;
+
+ void init(Temp *base, Temp *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);
+};
+
+struct Member: Expr {
+ Temp *base;
+ const QString *name;
+
+ void init(Temp *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);
+};
+
+struct Stmt {
+ enum Mode {
+ HIR,
+ MIR
+ };
+
+ struct Data {
+ QVector<unsigned> uses;
+ QVector<unsigned> defs;
+ QBitArray liveIn;
+ QBitArray liveOut;
+ };
+
+ Data *d;
+
+ Stmt(): d(0) {}
+ 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 Enter *asEnter() { return 0; }
+ virtual Leave *asLeave() { return 0; }
+ virtual Jump *asJump() { return 0; }
+ virtual CJump *asCJump() { return 0; }
+ virtual Ret *asRet() { return 0; }
+ virtual Try *asTry() { 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 Enter: Stmt {
+ Expr *expr;
+
+ void init(Expr *expr)
+ {
+ this->expr = expr;
+ }
+
+ virtual void accept(StmtVisitor *v) { v->visitEnter(this); }
+ virtual Enter *asEnter() { return this; }
+
+ virtual void dump(QTextStream &out, Mode);
+};
+
+struct Leave: Stmt {
+ void init() {}
+
+ virtual void accept(StmtVisitor *v) { v->visitLeave(this); }
+ virtual Leave *asLeave() { 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 {
+ Temp *expr;
+
+ void init(Temp *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 Q_V4_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;
+
+ 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(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;
+};
+
+struct BasicBlock {
+ Function *function;
+ QVector<Stmt *> statements;
+ QVector<BasicBlock *> in;
+ QVector<BasicBlock *> out;
+ QBitArray liveIn;
+ QBitArray liveOut;
+ int index;
+ int offset;
+
+ BasicBlock(Function *function): function(function), index(-1), 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;
+ }
+
+ unsigned newTemp();
+
+ Temp *TEMP(int index, uint scope = 0);
+
+ 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 *UNOP(AluOp op, Temp *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(Temp *base, Temp *index);
+ Expr *MEMBER(Temp *base, const QString *name);
+
+ Stmt *EXP(Expr *expr);
+ Stmt *ENTER(Expr *expr);
+ Stmt *LEAVE();
+
+ 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);
+};
+
+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 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/v4vm/qv4jsonobject.cpp b/src/qml/qml/v4vm/qv4jsonobject.cpp
new file mode 100644
index 0000000000..cb4df70970
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4jsonobject.cpp
@@ -0,0 +1,936 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+#include <qv4objectproto.h>
+#include <qv4numberobject.h>
+#include <qv4stringobject.h>
+#include <qv4booleanobject.h>
+#include <qv4objectiterator.h>
+#include <qjsondocument.h>
+#include <qstack.h>
+#include <qstringlist.h>
+
+#include <wtf/MathExtras.h>
+
+namespace QQmlJS {
+namespace VM {
+
+//#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 Parser
+{
+public:
+ Parser(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;
+
+
+Parser::Parser(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 Parser::eatSpace()
+{
+ while (json < end) {
+ if (*json > Space)
+ break;
+ if (*json != Space &&
+ *json != Tab &&
+ *json != LineFeed &&
+ *json != Return)
+ break;
+ ++json;
+ }
+ return (json < end);
+}
+
+QChar Parser::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 Parser::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 Parser::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 Parser::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 Parser::parseArray()
+{
+ BEGIN << "parseArray";
+ ArrayObject *array = context->engine->newArrayObject(context);
+
+ 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 Parser::parseValue(Value *val)
+{
+ BEGIN << "parse Value" << *json;
+
+ switch ((json++)->unicode()) {
+ case 'n':
+ if (end - json < 4) {
+ 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 < 4) {
+ 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 < 5) {
+ 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 Parser::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 Parser::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, ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject();
+ if (toJSON) {
+ Value arg = Value::fromString(ctx, key);
+ value = toJSON->call(ctx, 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(ctx, 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(ctx, o, ObjectIterator::EnumberableOnly);
+
+ while (1) {
+ String *name;
+ uint index;
+ PropertyAttributes attrs;
+ Property *pd = it.next(&name, &index, &attrs);
+ if (!pd)
+ break;
+ Value v = o->getValue(ctx, pd, attrs);
+ QString key;
+ if (name)
+ key = name->toQString();
+ else
+ key = QString::number(index);
+ 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(ctx, 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(ctx, 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;
+ Parser 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(ctx, 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);
+}
+
+
+
+}
+}
diff --git a/src/qml/qml/v4vm/qv4jsonobject.h b/src/qml/qml/v4vm/qv4jsonobject.h
new file mode 100644
index 0000000000..dba4786c2b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4jsonobject.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct JsonObject : Object {
+ JsonObject(ExecutionContext *context);
+
+ static Value method_parse(SimpleCallContext *ctx);
+ static Value method_stringify(SimpleCallContext *ctx);
+
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/qml/qml/v4vm/qv4lookup.cpp b/src/qml/qml/v4vm/qv4lookup.cpp
new file mode 100644
index 0000000000..38a11a99de
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4lookup.cpp
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+void Lookup::lookupPropertyGeneric(QQmlJS::VM::Lookup *l, ExecutionContext *ctx, QQmlJS::VM::Value *result, const QQmlJS::VM::Value &object)
+{
+ if (Object *o = object.asObject()) {
+ PropertyAttributes attrs;
+ Property *p = l->lookup(o, &attrs);
+ if (p) {
+ if (attrs.isData()) {
+ if (l->level == 0)
+ l->lookupProperty = lookupProperty0;
+ else if (l->level == 1)
+ l->lookupProperty = lookupProperty1;
+ else if (l->level == 2)
+ l->lookupProperty = lookupProperty2;
+ if (result)
+ *result = p->value;
+ return;
+ } else {
+ if (l->level == 0)
+ l->lookupProperty = lookupPropertyAccessor0;
+ else if (l->level == 1)
+ l->lookupProperty = lookupPropertyAccessor1;
+ else if (l->level == 2)
+ l->lookupProperty = lookupPropertyAccessor2;
+ if (result)
+ *result = p->value;
+ Value res = o->getValue(ctx, p, attrs);
+ if (result)
+ *result = res;
+ return;
+ }
+ } else if (result) {
+ *result = Value::undefinedValue();
+ }
+ } else {
+ Value res;
+ if (Managed *m = object.asManaged()) {
+ res = m->get(ctx, l->name);
+ } else {
+ o = __qmljs_convert_to_object(ctx, object);
+ res = o->get(ctx, l->name);
+ }
+ if (result)
+ *result = res;
+ }
+}
+
+void Lookup::lookupProperty0(Lookup *l, ExecutionContext *ctx, 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->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+void Lookup::lookupProperty1(Lookup *l, ExecutionContext *ctx, 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->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+void Lookup::lookupProperty2(Lookup *l, ExecutionContext *ctx, 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->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+void Lookup::lookupPropertyAccessor0(Lookup *l, ExecutionContext *ctx, 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(ctx, object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ l->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+void Lookup::lookupPropertyAccessor1(Lookup *l, ExecutionContext *ctx, 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(ctx, object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ l->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+void Lookup::lookupPropertyAccessor2(Lookup *l, ExecutionContext *ctx, 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(ctx, object, 0, 0);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ }
+ }
+ l->lookupProperty = lookupPropertyGeneric;
+ lookupPropertyGeneric(l, ctx, result, object);
+}
+
+
+void Lookup::lookupGlobalGeneric(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->lookupGlobal = lookupGlobal0;
+ else if (l->level == 1)
+ l->lookupGlobal = lookupGlobal1;
+ else if (l->level == 2)
+ l->lookupGlobal = lookupGlobal2;
+ *result = p->value;
+ return;
+ } else {
+ if (l->level == 0)
+ l->lookupGlobal = lookupGlobalAccessor0;
+ else if (l->level == 1)
+ l->lookupGlobal = lookupGlobalAccessor1;
+ else if (l->level == 2)
+ l->lookupGlobal = lookupGlobalAccessor2;
+ Value res = o->getValue(ctx, p, attrs);
+ if (result)
+ *result = res;
+ return;
+ }
+ }
+ ctx->throwReferenceError(Value::fromString(l->name));
+}
+
+void Lookup::lookupGlobal0(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->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+void Lookup::lookupGlobal1(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->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+void Lookup::lookupGlobal2(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->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+void Lookup::lookupGlobalAccessor0(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(ctx, Value::undefinedValue(), 0, 0);
+ return;
+ }
+ l->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+void Lookup::lookupGlobalAccessor1(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(ctx, Value::undefinedValue(), 0, 0);
+ return;
+ }
+ l->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+void Lookup::lookupGlobalAccessor2(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(ctx, Value::undefinedValue(), 0, 0);
+ return;
+ }
+ }
+ }
+ l->lookupGlobal = lookupGlobalGeneric;
+ lookupGlobalGeneric(l, ctx, result);
+}
+
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4vm/qv4lookup.h b/src/qml/qml/v4vm/qv4lookup.h
new file mode 100644
index 0000000000..38546cd65b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4lookup.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4runtime.h"
+#include "qv4engine.h"
+#include "qv4context.h"
+#include "qv4object.h"
+#include "qv4internalclass.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Lookup {
+ enum { Size = 3 };
+ union {
+ void (*lookupProperty)(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ void (*lookupGlobal)(Lookup *l, ExecutionContext *ctx, Value *result);
+ };
+ InternalClass *classList[Size];
+ int level;
+ uint index;
+ String *name;
+
+ static void lookupPropertyGeneric(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupProperty0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupProperty1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupProperty2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupPropertyAccessor0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupPropertyAccessor1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+ static void lookupPropertyAccessor2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object);
+
+ static void lookupGlobalGeneric(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobal0(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobal1(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobal2(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobalAccessor0(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobalAccessor1(Lookup *l, ExecutionContext *ctx, Value *result);
+ static void lookupGlobalAccessor2(Lookup *l, ExecutionContext *ctx, Value *result);
+
+ Property *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;
+ }
+
+ Property *setterLookup(Object *o, PropertyAttributes *attrs) {
+ if (o->internalClass == classList[0]) {
+ *attrs = o->internalClass->propertyData[index];
+ return o->memberData + index;
+ }
+
+ uint idx = o->internalClass->find(name);
+ if (idx != UINT_MAX) {
+ classList[0] = o->internalClass;
+ index = idx;
+ *attrs = o->internalClass->propertyData[index];
+ return o->memberData + index;
+ }
+ return 0;
+ }
+};
+
+}
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4managed.cpp b/src/qml/qml/v4vm/qv4managed.cpp
new file mode 100644
index 0000000000..ab87b9dd9f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4managed.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4mm.h"
+#include "qv4errorobject.h"
+
+using namespace QQmlJS::VM;
+
+const ManagedVTable Managed::static_vtbl =
+{
+ call,
+ construct,
+ 0 /*markObjects*/,
+ destroy,
+ hasInstance,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 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);
+}
+
+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 *, ExecutionContext *ctx, const Value &)
+{
+ ctx->throwTypeError();
+}
+
+Value Managed::construct(Managed *, ExecutionContext *context, Value *, int)
+{
+ context->throwTypeError();
+}
+
+Value Managed::call(Managed *, ExecutionContext *context, const Value &, Value *, int)
+{
+ context->throwTypeError();
+}
+
+Value Managed::get(ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ return vtbl->get(this, ctx, name, hasProperty);
+}
+
+Value Managed::getIndexed(ExecutionContext *ctx, uint index, bool *hasProperty)
+{
+ return vtbl->getIndexed(this, ctx, index, hasProperty);
+}
diff --git a/src/qml/qml/v4vm/qv4managed.h b/src/qml/qml/v4vm/qv4managed.h
new file mode 100644
index 0000000000..844c88772b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4managed.h
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+class MemoryManager;
+struct String;
+struct Object;
+struct ObjectPrototype;
+struct ExecutionContext;
+struct ScriptFunction;
+
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct ArrayObject;
+struct DateObject;
+struct FunctionObject;
+struct RegExpObject;
+struct ErrorObject;
+struct ArgumentsObject;
+struct JSONObject;
+struct ForeachIteratorObject;
+struct Managed;
+struct Value;
+struct RegExp;
+
+struct ManagedVTable
+{
+ Value (*call)(Managed *, ExecutionContext *context, const Value &thisObject, Value *args, int argc);
+ Value (*construct)(Managed *, ExecutionContext *context, Value *args, int argc);
+ void (*markObjects)(Managed *);
+ void (*destroy)(Managed *);
+ bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value &value);
+ Value (*get)(Managed *, ExecutionContext *ctx, String *name, bool *hasProperty);
+ Value (*getIndexed)(Managed *, ExecutionContext *ctx, uint index, bool *hasProperty);
+ void (*put)(Managed *, ExecutionContext *ctx, String *name, const Value &value);
+ void (*putIndexed)(Managed *, ExecutionContext *ctx, uint index, const Value &value);
+ PropertyAttributes (*query)(Managed *, ExecutionContext *ctx, String *name);
+ PropertyAttributes (*queryIndexed)(Managed *, ExecutionContext *ctx, uint index);
+ bool (*deleteProperty)(Managed *m, ExecutionContext *ctx, String *name);
+ bool (*deleteIndexedProperty)(Managed *m, ExecutionContext *ctx, uint index);
+ const char *className;
+};
+
+#define DEFINE_MANAGED_VTABLE(classname) \
+const ManagedVTable classname::static_vtbl = \
+{ \
+ call, \
+ construct, \
+ markObjects, \
+ destroy, \
+ hasInstance, \
+ get, \
+ getIndexed, \
+ put, \
+ putIndexed, \
+ query, \
+ queryIndexed, \
+ deleteProperty, \
+ deleteIndexedProperty, \
+ #classname \
+}
+
+
+struct Q_V4_EXPORT Managed
+{
+private:
+ void *operator new(size_t);
+ Managed(const Managed &other);
+ void operator = (const Managed &other);
+
+protected:
+ Managed()
+ : vtbl(&static_vtbl), _data(0)
+ { 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
+ };
+
+ String *asString() { return reinterpret_cast<String *>(this); }
+ Object *asObject() { return reinterpret_cast<Object *>(this); }
+ 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; }
+ RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast<RegExpObject *>(this) : 0; }
+ ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; }
+ ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; }
+ JSONObject *asJSONObject() { return type == Type_JSONObject ? reinterpret_cast<JSONObject *>(this) : 0; }
+ ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast<ForeachIteratorObject *>(this) : 0; }
+ RegExp *asRegExp() { return type == Type_RegExp ? reinterpret_cast<RegExp *>(this) : 0; }
+
+ 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(ExecutionContext *ctx, const Value &v) {
+ return vtbl->hasInstance(this, ctx, v);
+ }
+ Value construct(ExecutionContext *context, Value *args, int argc);
+ Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc);
+ Value get(ExecutionContext *ctx, String *name, bool *hasProperty = 0);
+ Value getIndexed(ExecutionContext *ctx, uint index, bool *hasProperty = 0);
+ void put(ExecutionContext *ctx, String *name, const Value &value)
+ { vtbl->put(this, ctx, name, value); }
+ void putIndexed(ExecutionContext *ctx, uint index, const Value &value)
+ { vtbl->putIndexed(this, ctx, index, value); }
+ bool deleteProperty(ExecutionContext *ctx, String *name)
+ { return vtbl->deleteProperty(this, ctx, name); }
+ bool deleteIndexedProperty(ExecutionContext *ctx, uint index)
+ { return vtbl->deleteIndexedProperty(this, ctx, index); }
+
+ static void destroy(Managed *that) { that->_data = 0; }
+ static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value);
+ static Value construct(Managed *, ExecutionContext *context, Value *, int);
+ static Value call(Managed *, ExecutionContext *, const Value &, Value *, int);
+
+ uint internalType() const {
+ return type;
+ }
+
+protected:
+
+ static const ManagedVTable static_vtbl;
+
+ const ManagedVTable *vtbl;
+
+ 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 : 5;
+ mutable uint subtype : 3;
+ uint externalComparison : 1;
+ uint unused : 15;
+ };
+ };
+
+private:
+ friend class MemoryManager;
+ friend struct Identifiers;
+};
+
+}
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4math.h b/src/qml/qml/v4vm/qv4math.h
new file mode 100644
index 0000000000..0699c0c971
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4math.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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
+
+#ifndef QMLJS_LLVM_RUNTIME
+# include <QtCore/qnumeric.h>
+#endif // QMLJS_LLVM_RUNTIME
+#include <cmath>
+
+#if !defined(QMLJS_LLVM_RUNTIME) && COMPILER(GCC) && (CPU(X86_64) || CPU(X86))
+#define QMLJS_INLINE_MATH
+#define QMLJS_READONLY __attribute((const))
+#endif
+
+#if defined(QMLJS_INLINE_MATH)
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+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);
+}
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // defined(QMLJS_INLINE_MATH)
+
+#ifdef QMLJS_READONLY
+#undef QMLJS_READONLY
+#endif
+
+#endif // QMLJS_MATH_H
diff --git a/src/qml/qml/v4vm/qv4mathobject.cpp b/src/qml/qml/v4vm/qv4mathobject.cpp
new file mode 100644
index 0000000000..d1017ebad6
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4mathobject.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4objectproto.h"
+
+#include <cmath>
+#include <qmath.h>
+#include <qnumeric.h>
+
+using namespace QQmlJS::VM;
+
+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 || 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) || 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 (isnan(y))
+ return Value::fromDouble(qSNaN());
+
+ if (y == 0) {
+ return Value::fromDouble(1);
+ } else if (((x == 1) || (x == -1)) && 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/v4vm/qv4mathobject.h b/src/qml/qml/v4vm/qv4mathobject.h
new file mode 100644
index 0000000000..68ca87d38b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4mathobject.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+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);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4vm/qv4mm.cpp b/src/qml/qml/v4vm/qv4mm.cpp
new file mode 100644
index 0000000000..19e0e3fd68
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4mm.cpp
@@ -0,0 +1,493 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4object.h"
+#include "qv4objectproto.h"
+#include "qv4mm.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
+
+using namespace QQmlJS::VM;
+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 = 256 };
+ 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 QQmlJS { namespace VM {
+
+bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b)
+{
+ return a.memory.base() < b.memory.base();
+}
+
+} } // namespace QQmlJS::VM
+
+MemoryManager::MemoryManager()
+ : m_d(new Data(true))
+ , m_contextList(0)
+{
+ setEnableGC(true);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_CREATE_MEMPOOL(this, 0, true);
+#endif
+
+#if 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)
+# if COMPILER(MSVC)
+ PNT_TIB tib = (PNT_TIB)NtCurrentTeb();
+ m_d->stackTop = static_cast<quintptr*>(tib->StackBase);
+# else
+# error "Unsupported compiler: no way to get the top-of-stack."
+# endif
+#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)*size;
+ 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();
+
+ // 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();
+}
+
+std::size_t MemoryManager::sweep()
+{
+ std::size_t freedCount = 0;
+
+ 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);
+
+ 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;
+ }
+
+ return freedCount;
+}
+
+std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size)
+{
+// 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
+ 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()
+{
+ sweep();
+}
+
+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();
+ }
+ }
+}
diff --git a/src/qml/qml/v4vm/qv4mm.h b/src/qml/qml/v4vm/qv4mm.h
new file mode 100644
index 0000000000..a41df835c3
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4mm.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4context.h"
+
+#include <QScopedPointer>
+
+//#define DETAILED_MM_STATS
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct ExecutionEngine;
+struct ExecutionContext;
+struct Managed;
+
+class Q_V4_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();
+ std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size);
+
+protected:
+ QScopedPointer<Data> m_d;
+ ExecutionContext *m_contextList;
+};
+
+inline ExecutionContext *MemoryManager::allocContext(uint size)
+{
+ ExecutionContext *newContext = (ExecutionContext *)malloc(size);
+ newContext->next = m_contextList;
+ m_contextList = newContext;
+ return newContext;
+}
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4GC_H
diff --git a/src/qml/qml/v4vm/qv4numberobject.cpp b/src/qml/qml/v4vm/qv4numberobject.cpp
new file mode 100644
index 0000000000..f32c8b4f97
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4numberobject.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <QtCore/QDebug>
+#include <cassert>
+
+
+using namespace QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(NumberCtor);
+
+NumberCtor::NumberCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value NumberCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc)
+{
+ double d = argc ? args[0].toNumber() : 0.;
+ return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d)));
+}
+
+Value NumberCtor::call(Managed *m, ExecutionContext *parentCtx, const Value &thisObject, 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 (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 (isnan(fdigits))
+ fdigits = 0;
+
+ if (fdigits < 0 || fdigits > 20)
+ ctx->throwRangeError(ctx->thisObject);
+
+ double v = thisObject->value.asDouble();
+ QString str;
+ if (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();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger();
+
+ QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits));
+ return Value::fromString(ctx, z);
+}
+
+Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger();
+
+ return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits)));
+}
diff --git a/src/qml/qml/v4vm/qv4numberobject.h b/src/qml/qml/v4vm/qv4numberobject.h
new file mode 100644
index 0000000000..d8be4790da
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4numberobject.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct NumberCtor: FunctionObject
+{
+ NumberCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4object.cpp b/src/qml/qml/v4vm/qv4object.cpp
new file mode 100644
index 0000000000..5091ceb095
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4object.cpp
@@ -0,0 +1,1177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto.h"
+#include "qv4stringobject.h"
+#include "qv4argumentsobject.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(Object);
+
+Object::Object(ExecutionEngine *engine)
+ : prototype(0)
+ , internalClass(engine->emptyClass)
+ , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+ , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+ , externalResource(0)
+{
+ vtbl = &static_vtbl;
+ type = Type_Object;
+}
+
+Object::Object(ExecutionContext *context)
+ : prototype(0)
+ , internalClass(context->engine->emptyClass)
+ , memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
+ , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0)
+ , externalResource(0)
+{
+ vtbl = &static_vtbl;
+ type = Type_Object;
+}
+
+Object::~Object()
+{
+ delete externalResource;
+ 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, ctx->engine->newString(name), value);
+}
+
+Value Object::getValue(const Value &thisObject, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs)
+{
+ if (!attrs.isAccessor())
+ return p->value;
+ FunctionObject *getter = p->getter();
+ if (!getter)
+ return Value::undefinedValue();
+
+ return getter->call(ctx, thisObject, 0, 0);
+}
+
+void Object::putValue(ExecutionContext *ctx, Property *pd, PropertyAttributes attrs, const Value &value)
+{
+ if (attrs.isAccessor()) {
+ if (pd->set) {
+ Value args[1];
+ args[0] = value;
+ pd->set->call(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+ goto reject;
+ }
+
+ if (!attrs.isWritable())
+ goto reject;
+
+ pd->value = value;
+ return;
+
+ reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+
+}
+
+void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs)
+{
+ Value v = get(ctx, name);
+ Value result;
+ op(ctx, &result, v, rhs);
+ put(ctx, 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(ctx, idx, &hasProperty);
+ Value result;
+ op(ctx, &result, v, rhs);
+ putIndexed(ctx, 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(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::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);
+ 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;
+}
+
+Value Object::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ return static_cast<Object *>(m)->internalGet(ctx, name, hasProperty);
+}
+
+Value Object::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty)
+{
+ return static_cast<Object *>(m)->internalGetIndexed(ctx, index, hasProperty);
+}
+
+void Object::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value)
+{
+ static_cast<Object *>(m)->internalPut(ctx, name, value);
+}
+
+void Object::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value)
+{
+ static_cast<Object *>(m)->internalPutIndexed(ctx, index, value);
+}
+
+PropertyAttributes Object::query(Managed *m, ExecutionContext *ctx, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return queryIndexed(m, ctx, idx);
+
+ const Object *o = static_cast<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(Managed *m, ExecutionContext *ctx, uint index)
+{
+ const Object *o = static_cast<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, ExecutionContext *ctx, String *name)
+{
+ return static_cast<Object *>(m)->internalDeleteProperty(ctx, name);
+}
+
+bool Object::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index)
+{
+ return static_cast<Object *>(m)->internalDeleteIndexedProperty(ctx, index);
+}
+
+
+// Section 8.12.3
+Value Object::internalGet(ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return getIndexed(ctx, idx, hasProperty);
+
+ name->makeIdentifier(ctx);
+
+ if (name->isEqualTo(ctx->engine->id___proto__)) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromObject(prototype);
+ }
+
+ Object *o = this;
+ while (o) {
+ uint idx = o->internalClass->find(name);
+ if (idx < UINT_MAX) {
+ if (hasProperty)
+ *hasProperty = true;
+ return getValue(ctx, o->memberData + idx, o->internalClass->propertyData.at(idx));
+ }
+
+ o = o->prototype;
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+Value Object::internalGetIndexed(ExecutionContext *ctx, 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(ctx, pd, attrs);
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+
+// Section 8.12.5
+void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return putIndexed(ctx, idx, value);
+
+ name->makeIdentifier(ctx);
+
+ 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(ctx->engine->id_length)) {
+ bool ok;
+ uint l = value.asArrayLength(&ok);
+ if (!ok)
+ ctx->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(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+
+ {
+ Property *p = insertMember(name, Attr_Data);
+ p->value = value;
+ return;
+ }
+
+ reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+}
+
+void Object::internalPutIndexed(ExecutionContext *ctx, 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(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+
+ arraySet(index, value);
+ return;
+
+ reject:
+ if (ctx->strictMode)
+ ctx->throwTypeError();
+}
+
+// Section 8.12.7
+bool Object::internalDeleteProperty(ExecutionContext *ctx, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != UINT_MAX)
+ return deleteIndexedProperty(ctx, idx);
+
+ name->makeIdentifier(ctx);
+
+ 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 (ctx->strictMode)
+ ctx->throwTypeError();
+ return false;
+ }
+
+ return true;
+}
+
+bool Object::internalDeleteIndexedProperty(ExecutionContext *ctx, 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 (ctx->strictMode)
+ ctx->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(ctx);
+
+ 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(ctx, 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(ctx, 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(ctx, 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(internalClass->engine->current, 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(context, arrayData + len, arrayAttributes[len]);
+ arrayAttributes[i] = Attr_Data;
+ arrayAttributes[len].clear();
+ } else if (arrayAttributes[i].isAccessor()) {
+ arrayData[i].value = getValue(context, arrayData + i, arrayAttributes[i]);
+ arrayAttributes[i] = Attr_Data;
+ }
+ }
+ }
+
+ ArrayElementLessThan lessThan(context, thisObject, comparefn);
+
+ Property *begin = arrayData;
+ std::sort(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::deletedValue();
+ 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::_Deleted_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::deletedValue();
+ }
+ }
+ 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();
+ }
+ }
+}
+
+void ArrayObject::init(ExecutionContext *context)
+{
+ type = Type_ArrayObject;
+ internalClass = context->engine->arrayClass;
+
+ memberData = new Property[4];
+ memberData[LengthPropertyIndex].value = Value::fromInt32(0);
+}
+
+
+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/v4vm/qv4object.h b/src/qml/qml/v4vm/qv4object.h
new file mode 100644
index 0000000000..d1bac5f03f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4object.h
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4runtime.h"
+#include "qv4engine.h"
+#include "qv4context.h"
+#include "qv4sparsearray.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4property.h"
+#include "qv4internalclass.h"
+#include "qv4objectiterator.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct Value;
+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;
+
+
+struct Q_V4_EXPORT Object: Managed {
+
+ class ExternalResource {
+ public:
+ virtual ~ExternalResource() {}
+ };
+
+ Object *prototype;
+ InternalClass *internalClass;
+ uint memberDataAlloc;
+ Property *memberData;
+
+ union {
+ uint arrayFreeList;
+ uint arrayOffset;
+ };
+ uint arrayDataLen;
+ uint arrayAlloc;
+ PropertyAttributes *arrayAttributes;
+ Property *arrayData;
+ SparseArray *sparseArray;
+ ExternalResource *externalResource;
+
+ enum {
+ InlinePropertySize = 4
+ };
+ Property inlineProperties[InlinePropertySize];
+
+ Object(ExecutionEngine *engine);
+ Object(ExecutionContext *context);
+ ~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 {
+ return __getPropertyDescriptor__(name);
+ }
+ 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, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs);
+ Value getValue(ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) const {
+ return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p, attrs);
+ }
+
+ void putValue(ExecutionContext *ctx, 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(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0);
+ /* 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);
+
+ // 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::_Deleted_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();
+
+ using Managed::get;
+ using Managed::getIndexed;
+ using Managed::put;
+ using Managed::putIndexed;
+ using Managed::deleteProperty;
+ using Managed::deleteIndexedProperty;
+protected:
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+ static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty);
+ static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value);
+ static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value);
+ static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name);
+ static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index);
+ static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name);
+ static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index);
+
+private:
+ Value internalGet(ExecutionContext *ctx, String *name, bool *hasProperty);
+ Value internalGetIndexed(ExecutionContext *ctx, uint index, bool *hasProperty);
+ void internalPut(ExecutionContext *ctx, String *name, const Value &value);
+ void internalPutIndexed(ExecutionContext *ctx, uint index, const Value &value);
+ bool internalDeleteProperty(ExecutionContext *ctx, String *name);
+ bool internalDeleteIndexedProperty(ExecutionContext *ctx, uint index);
+
+ friend struct ObjectIterator;
+ friend struct ObjectPrototype;
+};
+
+struct ForEachIteratorObject: Object {
+ ObjectIterator it;
+ ForEachIteratorObject(ExecutionContext *ctx, Object *o)
+ : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) {
+ vtbl = &static_vtbl;
+ type = Type_ForeachIteratorObject;
+ }
+
+ Value nextPropertyName() { return it.nextPropertyNameAsString(); }
+
+protected:
+ static const ManagedVTable static_vtbl;
+ 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(ExecutionContext *ctx) : Object(ctx->engine) { init(ctx); }
+ void init(ExecutionContext *context);
+};
+
+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);
+ }
+}
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4vm/qv4objectiterator.cpp b/src/qml/qml/v4vm/qv4objectiterator.cpp
new file mode 100644
index 0000000000..44ba9efcaa
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4objectiterator.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4object.h"
+#include "qv4stringobject.h"
+#include "qv4identifier.h"
+
+namespace QQmlJS {
+namespace VM {
+
+ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags)
+ : context(context)
+ , object(o)
+ , current(o)
+ , arrayNode(0)
+ , arrayIndex(0)
+ , memberIndex(0)
+ , flags(flags)
+{
+ if (current) {
+ if (current->asStringObject())
+ this->flags |= CurrentIsString;
+ }
+}
+
+Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs)
+{
+ Property *p = 0;
+ *name = 0;
+ *index = UINT_MAX;
+ while (1) {
+ if (!current)
+ break;
+
+ if (flags & CurrentIsString) {
+ StringObject *s = static_cast<StringObject *>(current);
+ uint slen = s->value.stringValue()->toQString().length();
+ while (arrayIndex < slen) {
+ *index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = s->arrayAttributes ? s->arrayAttributes[arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable);
+ return s->__getOwnProperty__(*index);
+ }
+ flags &= ~CurrentIsString;
+ arrayNode = current->sparseArrayBegin();
+ // iterate until we're past the end of the string
+ while (arrayNode && arrayNode->key() < slen)
+ arrayNode = arrayNode->nextNode();
+ }
+
+ if (!arrayIndex)
+ arrayNode = current->sparseArrayBegin();
+
+ // sparse arrays
+ if (arrayNode) {
+ while (arrayNode != current->sparseArrayEnd()) {
+ int k = arrayNode->key();
+ uint pidx = arrayNode->value;
+ p = current->arrayData + pidx;
+ arrayNode = arrayNode->nextNode();
+ PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ if (!(flags & EnumberableOnly) || a.isEnumerable()) {
+ arrayIndex = k + 1;
+ *index = k;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+ arrayNode = 0;
+ arrayIndex = UINT_MAX;
+ }
+ // dense arrays
+ while (arrayIndex < current->arrayDataLen) {
+ uint pidx = current->propertyIndexFromArrayIndex(arrayIndex);
+ p = current->arrayData + pidx;
+ PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data);
+ ++arrayIndex;
+ if ((!current->arrayAttributes || !current->arrayAttributes[pidx].isGeneric())
+ && (!(flags & EnumberableOnly) || a.isEnumerable())) {
+ *index = arrayIndex - 1;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+
+ if (memberIndex == current->internalClass->size) {
+ if (flags & WithProtoChain)
+ current = current->prototype;
+ else
+ current = 0;
+ if (current && current->asStringObject())
+ flags |= CurrentIsString;
+ else
+ flags &= ~CurrentIsString;
+
+
+ arrayIndex = 0;
+ memberIndex = 0;
+ continue;
+ }
+ String *n = current->internalClass->nameMap.at(memberIndex);
+ assert(n);
+ // ### check that it's not a repeated attribute
+
+ p = current->memberData + memberIndex;
+ PropertyAttributes a = current->internalClass->propertyData[memberIndex];
+ ++memberIndex;
+ if (!(flags & EnumberableOnly) || a.isEnumerable()) {
+ *name = n;
+ if (attrs)
+ *attrs = a;
+ return p;
+ }
+ }
+ return 0;
+}
+
+Value ObjectIterator::nextPropertyName()
+{
+ uint index;
+ String *name;
+ next(&name, &index);
+ if (name)
+ return Value::fromString(name);
+ if (index < UINT_MAX)
+ return Value::fromDouble(index);
+ return Value::nullValue();
+}
+
+Value ObjectIterator::nextPropertyNameAsString()
+{
+ uint index;
+ String *name;
+ next(&name, &index);
+ if (name)
+ return Value::fromString(name);
+ if (index < UINT_MAX)
+ return __qmljs_to_string(Value::fromDouble(index), context);
+ return Value::nullValue();
+}
+
+}
+}
+
diff --git a/src/qml/qml/v4vm/qv4objectiterator.h b/src/qml/qml/v4vm/qv4objectiterator.h
new file mode 100644
index 0000000000..c740132661
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4objectiterator.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "qv4value.h"
+#include <qv4internalclass.h>
+#include <qv4property.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct SparseArrayNode;
+struct Object;
+
+struct ObjectIterator
+{
+ enum Flags {
+ NoFlags = 0,
+ EnumberableOnly = 0x1,
+ WithProtoChain = 0x2,
+ CurrentIsString = 0x4
+ };
+
+ ExecutionContext *context;
+ Object *object;
+ Object *current;
+ SparseArrayNode *arrayNode;
+ uint arrayIndex;
+ uint memberIndex;
+ uint flags;
+
+ ObjectIterator(ExecutionContext *context, Object *o, uint flags);
+ Property *next(String **name, uint *index, PropertyAttributes *attributes = 0);
+ Value nextPropertyName();
+ Value nextPropertyNameAsString();
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4objectproto.cpp b/src/qml/qml/v4vm/qv4objectproto.cpp
new file mode 100644
index 0000000000..df3d4c8e43
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4objectproto.cpp
@@ -0,0 +1,565 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+
+DEFINE_MANAGED_VTABLE(ObjectCtor);
+
+ObjectCtor::ObjectCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value ObjectCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc)
+{
+ ObjectCtor *ctor = static_cast<ObjectCtor *>(that);
+ if (!argc || args[0].isUndefined() || args[0].isNull()) {
+ Object *obj = ctx->engine->newObject();
+ Value proto = ctor->get(ctx, ctx->engine->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+ return Value::fromObject(obj);
+ }
+ return __qmljs_to_object(ctx, args[0]);
+}
+
+Value ObjectCtor::call(Managed *, ExecutionContext *ctx, const Value &/*thisObject*/, Value *args, int argc)
+{
+ if (!argc || args[0].isUndefined() || args[0].isNull())
+ return Value::fromObject(ctx->engine->newObject());
+ return __qmljs_to_object(ctx, 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, 0);
+ defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0);
+}
+
+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 = context->engine->newArrayObject(context)->asArrayObject();
+ ObjectIterator it(context, O, ObjectIterator::NoFlags);
+ while (1) {
+ Value v = it.nextPropertyNameAsString();
+ if (v.isNull())
+ break;
+ array->push_back(v);
+ }
+ 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(ctx, o, ObjectIterator::EnumberableOnly);
+ 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(ctx, 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(ctx);
+
+ ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly);
+ while (1) {
+ uint index;
+ String *name;
+ Property *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ Value key;
+ if (name) {
+ key = Value::fromString(name);
+ } else {
+ key = Value::fromDouble(index);
+ key = __qmljs_to_string(key, ctx);
+ }
+ a->push_back(key);
+ }
+
+ 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, ctx->engine->newString(QStringLiteral("toString")));
+ FunctionObject *f = ts.asFunctionObject();
+ if (!f)
+ ctx->throwTypeError();
+ return f->call(ctx, 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.toObject(ctx);
+
+ 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.toObject(ctx);
+
+ Property pd = Property::fromAccessor(0, f);
+ o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor);
+ 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, ctx->engine->id_enumerable).toBoolean());
+
+ if (o->__hasProperty__(ctx->engine->id_configurable))
+ attrs->setConfigurable(o->get(ctx, ctx->engine->id_configurable).toBoolean());
+
+ if (o->__hasProperty__(ctx->engine->id_get)) {
+ Value get = o->get(ctx, 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, 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, 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, ctx->engine->id_value);
+ attrs->setType(PropertyAttributes::Data);
+ }
+
+ if (attrs->isGeneric())
+ desc->value = Value::deletedValue();
+}
+
+
+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);
+}
diff --git a/src/qml/qml/v4vm/qv4objectproto.h b/src/qml/qml/v4vm/qv4objectproto.h
new file mode 100644
index 0000000000..0a19f45b06
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4objectproto.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct ObjectCtor: FunctionObject
+{
+ ObjectCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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 void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs);
+ static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4property.h b/src/qml/qml/v4vm/qv4property.h
new file mode 100644
index 0000000000..afb5ede277
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4property.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4value.h"
+#include "qv4internalclass.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+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::deletedValue();
+ 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;
+ }
+}
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4regexp.cpp b/src/qml/qml/v4vm/qv4regexp.cpp
new file mode 100644
index 0000000000..c0f7cee51d
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4regexp.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+#include "qv4engine.h"
+
+namespace QQmlJS {
+namespace VM {
+
+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) const
+{
+ if (!isValid())
+ return JSC::Yarr::offsetNoMatch;
+
+ return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).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)
+ : 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);
+}
+
+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, ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ return Value::undefinedValue();
+}
+
+Value RegExp::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty)
+{
+ return Value::undefinedValue();
+}
+
+void RegExp::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value)
+{
+}
+
+void RegExp::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value)
+{
+}
+
+PropertyAttributes RegExp::query(Managed *m, ExecutionContext *ctx, String *name)
+{
+ return Attr_Invalid;
+}
+
+PropertyAttributes RegExp::queryIndexed(Managed *m, ExecutionContext *ctx, uint index)
+{
+ return Attr_Invalid;
+}
+
+bool RegExp::deleteProperty(Managed *m, ExecutionContext *ctx, String *name)
+{
+ return false;
+}
+
+bool RegExp::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index)
+{
+ return false;
+}
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+
diff --git a/src/qml/qml/v4vm/qv4regexp.h b/src/qml/qml/v4vm/qv4regexp.h
new file mode 100644
index 0000000000..b0c95843f3
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4regexp.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "qv4managed.h"
+#include "qv4engine.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+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
+{
+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) const;
+
+ bool ignoreCase() const { return m_ignoreCase; }
+ bool multiLine() const { return m_multiLine; }
+ int captureCount() const { return m_subPatternCount + 1; }
+
+protected:
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+ static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty);
+ static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value);
+ static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value);
+ static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name);
+ static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index);
+ static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name);
+ static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index);
+
+
+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;
+ 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())
+{}
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4REGEXP_H
diff --git a/src/qml/qml/v4vm/qv4regexpobject.cpp b/src/qml/qml/v4vm/qv4regexpobject.cpp
new file mode 100644
index 0000000000..3119382e69
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4regexpobject.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4jsir_p.h"
+#include "qv4isel_p.h"
+#include "qv4objectproto.h"
+#include "qv4stringobject.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(RegExpObject);
+
+RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global)
+ : Object(engine)
+ , value(value)
+ , global(global)
+{
+ 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;
+ defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern())));
+ 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];
+}
+
+DEFINE_MANAGED_VTABLE(RegExpCtor);
+
+RegExpCtor::RegExpCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ vtbl = &static_vtbl;
+}
+
+Value RegExpCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc)
+{
+ Value r = argc > 0 ? argv[0] : Value::undefinedValue();
+ Value f = argc > 1 ? argv[1] : Value::undefinedValue();
+ if (RegExpObject *re = r.asRegExpObject()) {
+ if (!f.isUndefined())
+ ctx->throwTypeError();
+
+ RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global);
+ return Value::fromObject(o);
+ }
+
+ if (r.isUndefined())
+ r = Value::fromString(ctx, QString());
+ else if (!r.isString())
+ r = __qmljs_to_string(r, ctx);
+
+ 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, r.stringValue()->toQString(), ignoreCase, multiLine);
+ if (!re->isValid())
+ ctx->throwSyntaxError(0);
+
+ RegExpObject *o = ctx->engine->newRegExpObject(re, global);
+ return Value::fromObject(o);
+}
+
+Value RegExpCtor::call(Managed *that, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc)
+{
+ if (argc > 0 && argv[0].asRegExpObject()) {
+ if (argc == 1 || argv[1].isUndefined())
+ return argv[0];
+ }
+
+ return construct(that, ctx, 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.asRegExpObject();
+ 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(ctx)->asArrayObject();
+ 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.asRegExpObject();
+ if (!r)
+ ctx->throwTypeError();
+
+ QString result = QChar('/') + r->value->pattern();
+ result += QChar('/');
+ // ### 'g' option missing
+ if (r->value->ignoreCase())
+ result += QChar('i');
+ if (r->value->multiLine())
+ result += QChar('m');
+ return Value::fromString(ctx, result);
+}
+
+Value RegExpPrototype::method_compile(SimpleCallContext *ctx)
+{
+ RegExpObject *r = ctx->thisObject.asRegExpObject();
+ if (!r)
+ ctx->throwTypeError();
+
+ RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject();
+
+ r->value = re->value;
+ r->global = re->global;
+ return Value::undefinedValue();
+}
+
diff --git a/src/qml/qml/v4vm/qv4regexpobject.h b/src/qml/qml/v4vm/qv4regexpobject.h
new file mode 100644
index 0000000000..df4190ba77
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4regexpobject.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4engine.h"
+#include "qv4context.h"
+#include "qv4functionobject.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4property.h"
+#include "qv4objectiterator.h"
+#include "qv4regexp.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct RegExpObject: Object {
+ RegExp* value;
+ Property *lastIndexProperty(ExecutionContext *ctx);
+ bool global;
+ RegExpObject(ExecutionEngine *engine, RegExp* value, bool global);
+ ~RegExpObject() {}
+
+protected:
+ static const ManagedVTable static_vtbl;
+ static void destroy(Managed *that);
+ static void markObjects(Managed *that);
+};
+
+
+struct RegExpCtor: FunctionObject
+{
+ RegExpCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, 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);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/qml/qml/v4vm/qv4runtime.cpp b/src/qml/qml/v4vm/qv4runtime.cpp
new file mode 100644
index 0000000000..9cd92677f6
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4runtime.cpp
@@ -0,0 +1,1319 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "debugging.h"
+#include "qv4runtime.h"
+#include "qv4object.h"
+#include "qv4jsir_p.h"
+#include "qv4objectproto.h"
+#include "qv4globalobject.h"
+#include "qv4stringobject.h"
+#include "qv4lookup.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"
+
+#if USE(LIBUNWIND_DEBUG)
+#include <libunwind.h>
+#include <execinfo.h>
+#endif
+
+namespace QQmlJS {
+namespace VM {
+
+QString numberToString(double num, int radix = 10)
+{
+ if (isnan(num)) {
+ return QStringLiteral("NaN");
+ } else if (qIsInf(num)) {
+ return QLatin1String(num < 0 ? "-Infinity" : "Infinity");
+ }
+
+ if (radix == 10) {
+ char str[100];
+ double_conversion::StringBuilder builder(str, sizeof(str));
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder);
+ return QString::fromLatin1(builder.Finalize());
+ }
+
+ 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 str;
+}
+
+Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue)
+{
+ this->throwingContext = throwingContext->engine->current;
+ this->exception = PersistentValue(throwingContext->engine->memoryManager, exceptionValue);
+ accepted = false;
+}
+
+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;
+}
+
+extern "C" {
+
+void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::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(ctx, 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(ctx, 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(ctx, 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 String *string)
+{
+ QString s = string->toQString();
+ s = s.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)
+{
+ String *string = ctx->engine->newString(numberToString(number, 10));
+ 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(ctx, meth1);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(ctx, Value::fromObject(object), 0, 0);
+ if (r.isPrimitive())
+ return r;
+ }
+
+ conv = object->get(ctx, meth2);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(ctx, 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(ctx, 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(ctx, 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;
+ }
+ }
+
+ 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(ctx, idx);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ String *name = index.toString(ctx);
+ Value res = o->get(ctx, 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(ctx, Value::fromObject(o), args, 1);
+ return;
+ }
+ }
+ o->putIndexed(ctx, idx, value);
+ return;
+ }
+
+ String *name = index.toString(ctx);
+ o->put(ctx, 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->asForeachIteratorObject());
+
+ *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(ctx, name);
+ } else {
+ m = __qmljs_convert_to_object(ctx, object);
+ res = m->get(ctx, name);
+ }
+ if (result)
+ *result = res;
+}
+
+void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name)
+{
+ *result = ctx->getProperty(name);
+}
+
+void __qmljs_get_global_lookup(ExecutionContext *ctx, Value *result, int lookupIndex)
+{
+ Lookup *l = ctx->lookups + lookupIndex;
+ l->lookupGlobal(l, ctx, result);
+}
+
+void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex)
+{
+ Lookup *l = ctx->lookups + lookupIndex;
+ l->lookupProperty(l, ctx, result, object);
+}
+
+void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value)
+{
+ Object *o = object.toObject(ctx);
+ Lookup *l = ctx->lookups + lookupIndex;
+
+ PropertyAttributes attrs;
+ Property *p = l->setterLookup(o, &attrs);
+ if (p && (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject())) {
+ o->putValue(ctx, p, attrs, value);
+ return;
+ }
+
+ o->put(ctx, l->name, value);
+}
+
+
+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()->externalComparison || y.objectValue()->externalComparison)
+ return x.objectValue()->externalComparison && y.objectValue()->externalComparison
+ && x.objectValue()->internalClass->engine->externalResourceComparison(x, y);
+ return x.objectValue() == 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.isDouble() || y.isDouble())
+ return x.asDouble() == y.asDouble();
+ if (x.rawValue() == y.rawValue())
+ return true;
+ if (x.type() != y.type())
+ return false;
+ if (x.isString())
+ return x.stringValue()->isEqualTo(y.stringValue());
+ if (x.isObject() && x.objectValue()->externalComparison && y.objectValue()->externalComparison)
+ return x.objectValue()->internalClass->engine->externalResourceComparison(x, y);
+ 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->lookupGlobal(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(context, thisObject, args, argc, true);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ Value res = o->call(context, 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)
+ context->throwTypeError();
+
+ 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(context, thisObject, args, argc, true);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ Value res = o->call(context, 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) {
+ baseObject = __qmljs_convert_to_object(context, thisObject);
+ thisObject = Value::fromObject(static_cast<Object *>(baseObject));
+ }
+
+ Value func = baseObject->get(context, name);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value res = o->call(context, 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(context, p, attrs);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value res = o->call(context, 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(context, index.toString(context));
+ Object *o = func.asObject();
+ if (!o)
+ context->throwTypeError();
+
+ Value res = o->call(context, 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(context, 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->lookupGlobal(l, context, &func);
+
+ if (Object *f = func.asObject()) {
+ Value res = f->construct(context, 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(context, 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(context, name);
+ if (Object *f = func.asObject()) {
+ Value res = f->construct(context, args, argc);
+ if (result)
+ *result = res;
+ return;
+ }
+
+ context->throwTypeError();
+}
+
+void __qmljs_throw(ExecutionContext *context, const Value &value)
+{
+ if (context->engine->debugger)
+ context->engine->debugger->aboutToThrow(value);
+
+#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
+
+ throw Exception(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(context, 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(context, 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(context, 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(context, 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(context, 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(context, 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(context, 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(context, 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(context, 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(context, idx, v);
+}
+
+void __qmljs_builtin_throw(ExecutionContext *context, const Value &val)
+{
+ __qmljs_throw(context, val);
+}
+
+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(ctx);
+
+ // ### 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].isDeleted()) {
+ 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_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);
+ }
+}
+
+} // extern "C"
+
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/qml/qml/v4vm/qv4runtime.h b/src/qml/qml/v4vm/qv4runtime.h
new file mode 100644
index 0000000000..6e64c44e69
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4runtime.h
@@ -0,0 +1,745 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4value.h"
+#include "qv4math.h"
+
+
+#include <QtCore/QString>
+#include <QtCore/qnumeric.h>
+#include <QtCore/QDebug>
+
+#include <cmath>
+#include <cassert>
+
+#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 QQmlJS {
+namespace VM {
+
+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 Q_V4_EXPORT Exception {
+ explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue);
+ ~Exception();
+
+ void accept(ExecutionContext *catchingContext);
+
+ void partiallyUnwindContext(ExecutionContext *catchingContext);
+
+ Value value() const { return exception; }
+
+private:
+ ExecutionContext *throwingContext;
+ bool accepted;
+ PersistentValue exception;
+};
+
+extern "C" {
+
+// context
+void __qmljs_call_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc);
+void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &that, String *name, Value *args, int argc);
+void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc);
+void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc);
+void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc);
+
+void __qmljs_construct_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc);
+void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc);
+void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc);
+
+void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &val);
+void __qmljs_builtin_typeof_name(ExecutionContext *context, Value* result, String *name);
+void __qmljs_builtin_typeof_member(ExecutionContext* context, Value* result, const Value &base, String *name);
+void __qmljs_builtin_typeof_element(ExecutionContext* context, Value *result, const Value &base, const Value &index);
+
+void __qmljs_builtin_post_increment(Value *result, Value *val);
+void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name);
+void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name);
+void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index);
+
+void __qmljs_builtin_post_decrement(Value *result, Value *val);
+void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name);
+void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name);
+void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index);
+
+void Q_NORETURN __qmljs_builtin_throw(ExecutionContext *context, const Value &val);
+void Q_NORETURN __qmljs_builtin_rethrow(ExecutionContext *context);
+ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx);
+ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const QQmlJS::VM::Value &exceptionValue, ExecutionContext *ctx);
+ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx);
+void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name);
+void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val);
+void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, QQmlJS::VM::Value *values, uint length);
+void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter);
+
+// constructors
+void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos);
+VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name,
+ bool hasDirectEval,
+ bool usesArgumentsObject, bool isStrict,
+ bool hasNestedFunctions,
+ String **formals, unsigned formalCount,
+ String **locals, unsigned localCount);
+
+
+// strings
+double __qmljs_string_to_number(const String *string);
+Value __qmljs_string_from_number(ExecutionContext *ctx, double number);
+String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second);
+
+// objects
+Value __qmljs_object_default_value(Object *object, int typeHint);
+void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value& value);
+void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value);
+void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name);
+void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name);
+
+void __qmljs_get_global_lookup(ExecutionContext *ctx, Value *result, int lookupIndex);
+void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc);
+void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc);
+void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex);
+void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value);
+
+
+void __qmljs_get_element(ExecutionContext *ctx, Value *retval, const Value &object, const Value &index);
+void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value);
+
+// For each
+void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in);
+void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator);
+
+// type conversion and testing
+Value __qmljs_to_primitive(const Value &value, int typeHint);
+Bool __qmljs_to_boolean(const Value &value);
+double __qmljs_to_number(const Value &value);
+Value __qmljs_to_string(const Value &value, ExecutionContext *ctx);
+Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value);
+Value __qmljs_to_object(ExecutionContext *ctx, const Value &value);
+Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value);
+
+Bool __qmljs_equal(const Value &x, const Value &y);
+Bool __qmljs_strict_equal(const Value &x, const Value &y);
+
+// unary operators
+typedef void (*UnaryOpName)(Value *, const Value &);
+void __qmljs_uplus(Value *result, const Value &value);
+void __qmljs_uminus(Value *result, const Value &value);
+void __qmljs_compl(Value *result, const Value &value);
+void __qmljs_not(Value *result, const Value &value);
+void __qmljs_increment(Value *result, const Value &value);
+void __qmljs_decrement(Value *result, const Value &value);
+
+void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index);
+void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name);
+void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name);
+
+void Q_NORETURN __qmljs_throw(ExecutionContext*, const Value &value);
+
+// binary operators
+typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+
+void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_bit_or(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_bit_xor(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_bit_and(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_sub(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_mul(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_div(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_mod(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_shl(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_shr(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_ushr(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+void __qmljs_se(ExecutionContext *, Value *result, const Value &left, const Value &right);
+void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, const Value &right);
+
+void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right);
+
+
+typedef void (*InplaceBinOpName)(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value);
+void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value);
+
+typedef void (*InplaceBinOpElement)(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs);
+
+typedef void (*InplaceBinOpMember)(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs);
+
+typedef Bool (*CmpOp)(ExecutionContext *ctx, const Value &left, const Value &right);
+Bool __qmljs_cmp_gt(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_lt(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_ge(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_le(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_eq(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_ne(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_se(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_sne(ExecutionContext *, const Value &left, const Value &right);
+Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right);
+Bool __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right);
+
+// type conversion and testing
+inline Value __qmljs_to_primitive(const Value &value, int typeHint)
+{
+ Object *o = value.asObject();
+ if (!o)
+ return value;
+ return __qmljs_object_default_value(o, typeHint);
+}
+
+inline double __qmljs_to_number(const Value &value)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ return std::numeric_limits<double>::quiet_NaN();
+ case Value::Null_Type:
+ return 0;
+ case Value::Boolean_Type:
+ return (value.booleanValue() ? 1. : 0.);
+ case Value::Integer_Type:
+ return value.int_32;
+ case Value::String_Type:
+ return __qmljs_string_to_number(value.stringValue());
+ case Value::Object_Type: {
+ Value prim = __qmljs_to_primitive(value, NUMBER_HINT);
+ return __qmljs_to_number(prim);
+ }
+ default: // double
+ return value.doubleValue();
+ }
+}
+
+inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx)
+{
+ if (value.isString())
+ return value;
+ return Value::fromString(__qmljs_convert_to_string(ctx, value));
+}
+
+inline Value __qmljs_to_object(ExecutionContext *ctx, const Value &value)
+{
+ if (value.isObject())
+ return value;
+ return Value::fromObject(__qmljs_convert_to_object(ctx, value));
+}
+
+
+inline void __qmljs_uplus(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ *result = value;
+ if (result->tryIntegerConversion())
+ return;
+
+ double n = __qmljs_to_number(value);
+ *result = Value::fromDouble(n);
+}
+
+inline void __qmljs_uminus(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ // +0 != -0, so we need to convert to double when negating 0
+ if (value.isInteger() && value.integerValue())
+ *result = Value::fromInt32(-value.integerValue());
+ else {
+ double n = __qmljs_to_number(value);
+ *result = Value::fromDouble(-n);
+ }
+}
+
+inline void __qmljs_compl(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ int n;
+ if (value.isConvertibleToInt())
+ n = value.int_32;
+ else
+ n = Value::toInt32(__qmljs_to_number(value));
+
+ *result = Value::fromInt32(~n);
+}
+
+inline void __qmljs_not(Value *result, const Value &value)
+{
+ TRACE1(value);
+
+ bool b = value.toBoolean();
+ *result = Value::fromBoolean(!b);
+}
+
+// binary operators
+inline void __qmljs_bit_or(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ *result = Value::fromInt32(left.integerValue() | right.integerValue());
+ return;
+ }
+
+ int lval = Value::toInt32(__qmljs_to_number(left));
+ int rval = Value::toInt32(__qmljs_to_number(right));
+ *result = Value::fromInt32(lval | rval);
+}
+
+inline void __qmljs_bit_xor(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ *result = Value::fromInt32(left.integerValue() ^ right.integerValue());
+ return;
+ }
+
+ int lval = Value::toInt32(__qmljs_to_number(left));
+ int rval = Value::toInt32(__qmljs_to_number(right));
+ *result = Value::fromInt32(lval ^ rval);
+}
+
+inline void __qmljs_bit_and(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ *result = Value::fromInt32(left.integerValue() & right.integerValue());
+ return;
+ }
+
+ int lval = Value::toInt32(__qmljs_to_number(left));
+ int rval = Value::toInt32(__qmljs_to_number(right));
+ *result = Value::fromInt32(lval & rval);
+}
+
+inline void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+#ifdef QMLJS_INLINE_MATH
+ if (Value::integerCompatible(left, right)) {
+ *result = add_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+#endif
+
+ if (Value::bothDouble(left, right)) {
+ *result = Value::fromDouble(left.doubleValue() + right.doubleValue());
+ return;
+ }
+
+ __qmljs_add_helper(ctx, result, left, right);
+}
+
+inline void __qmljs_sub(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+#ifdef QMLJS_INLINE_MATH
+ if (Value::integerCompatible(left, right)) {
+ *result = sub_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+#endif
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = Value::fromDouble(lval - rval);
+}
+
+inline void __qmljs_mul(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+#ifdef QMLJS_INLINE_MATH
+ if (Value::integerCompatible(left, right)) {
+ *result = mul_int32(left.integerValue(), right.integerValue());
+ return;
+ }
+#endif
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = Value::fromDouble(lval * rval);
+}
+
+inline void __qmljs_div(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = Value::fromDouble(lval / rval);
+}
+
+inline void __qmljs_mod(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right) && right.integerValue() != 0) {
+ int intRes = left.integerValue() % right.integerValue();
+ if (intRes != 0 || left.integerValue() >= 0) {
+ *result = Value::fromInt32(intRes);
+ return;
+ }
+ }
+
+ double lval = __qmljs_to_number(left);
+ double rval = __qmljs_to_number(right);
+ *result = Value::fromDouble(fmod(lval, rval));
+}
+
+inline void __qmljs_shl(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ *result = Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f)));
+ return;
+ }
+
+ int lval = Value::toInt32(__qmljs_to_number(left));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ *result = Value::fromInt32(lval << rval);
+}
+
+inline void __qmljs_shr(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ *result = Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f)));
+ return;
+ }
+
+ int lval = Value::toInt32(__qmljs_to_number(left));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ *result = Value::fromInt32(lval >> rval);
+}
+
+inline void __qmljs_ushr(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ uint res;
+ if (Value::integerCompatible(left, right)) {
+ res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f);
+ } else {
+ unsigned lval = Value::toUInt32(__qmljs_to_number(left));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f;
+ res = lval >> rval;
+ }
+
+ if (res > INT_MAX)
+ *result = Value::fromDouble(res);
+ else
+ *result = Value::fromInt32(res);
+}
+
+inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right));
+}
+
+inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right));
+}
+
+inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right));
+}
+
+inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(__qmljs_cmp_le(ctx, left, right));
+}
+
+inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right));
+}
+
+inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ *result = Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right));
+}
+
+inline void __qmljs_se(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = __qmljs_strict_equal(left, right);
+ *result = Value::fromBoolean(r);
+}
+
+inline void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = ! __qmljs_strict_equal(left, right);
+ *result = Value::fromBoolean(r);
+}
+
+inline Bool __qmljs_cmp_gt(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() > right.integerValue();
+
+ Value l = __qmljs_to_primitive(left, NUMBER_HINT);
+ Value r = __qmljs_to_primitive(right, NUMBER_HINT);
+
+ if (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 Bool __qmljs_cmp_lt(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() < right.integerValue();
+
+ Value l = __qmljs_to_primitive(left, NUMBER_HINT);
+ Value r = __qmljs_to_primitive(right, NUMBER_HINT);
+
+ if (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 Bool __qmljs_cmp_ge(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() >= right.integerValue();
+
+ Value l = __qmljs_to_primitive(left, NUMBER_HINT);
+ Value r = __qmljs_to_primitive(right, NUMBER_HINT);
+
+ if (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 Bool __qmljs_cmp_le(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() <= right.integerValue();
+
+ Value l = __qmljs_to_primitive(left, NUMBER_HINT);
+ Value r = __qmljs_to_primitive(right, NUMBER_HINT);
+
+ if (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 Bool __qmljs_cmp_eq(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ // need to test for doubles first as NaN != NaN
+ if (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 Bool __qmljs_cmp_ne(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return !__qmljs_cmp_eq(0, left, right);
+}
+
+inline Bool __qmljs_cmp_se(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return __qmljs_strict_equal(left, right);
+}
+
+inline Bool __qmljs_cmp_sne(ExecutionContext *, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return ! __qmljs_strict_equal(left, right);
+}
+
+inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ Value v;
+ __qmljs_instanceof(ctx, &v, left, right);
+ return v.booleanValue();
+}
+
+inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ Value v;
+ __qmljs_in(ctx, &v, left, right);
+ return v.booleanValue();
+}
+
+} // extern "C"
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QMLJS_RUNTIME_H
diff --git a/src/qml/qml/v4vm/qv4sparsearray.cpp b/src/qml/qml/v4vm/qv4sparsearray.cpp
new file mode 100644
index 0000000000..2c9075ba5b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4sparsearray.cpp
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 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.h"
+#include "qv4runtime.h"
+#include "qv4object.h"
+#include "qv4functionobject.h"
+#include <stdlib.h>
+
+#ifdef QT_QMAP_DEBUG
+# include <qstring.h>
+# include <qvector.h>
+#endif
+
+namespace QQmlJS {
+namespace VM {
+
+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/v4vm/qv4sparsearray.h b/src/qml/qml/v4vm/qv4sparsearray.h
new file mode 100644
index 0000000000..a6f7d40c38
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4sparsearray.h
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 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.h"
+#include <QtCore/qmap.h>
+#include <qv4value.h>
+#include <qv4property.h>
+#include <assert.h>
+
+#ifdef Q_MAP_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+#include <new>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+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_V4_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/v4vm/qv4string.cpp b/src/qml/qml/v4vm/qv4string.cpp
new file mode 100644
index 0000000000..cb17547f07
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4string.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4identifier.h"
+#include "qv4runtime.h"
+#include "qv4objectproto.h"
+#include "qv4stringobject.h"
+#include <QtCore/QHash>
+
+namespace QQmlJS {
+namespace VM {
+
+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;
+}
+
+const ManagedVTable String::static_vtbl =
+{
+ call,
+ construct,
+ 0 /*markObjects*/,
+ destroy,
+ hasInstance,
+ get,
+ getIndexed,
+ put,
+ putIndexed,
+ query,
+ queryIndexed,
+ deleteProperty,
+ deleteIndexedProperty,
+ "String",
+};
+
+void String::destroy(Managed *that)
+{
+ static_cast<String*>(that)->~String();
+}
+
+Value String::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ String *that = static_cast<String *>(m);
+ if (name == ctx->engine->id_length) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromInt32(that->_text.length());
+ }
+ PropertyAttributes attrs;
+ Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(name, &attrs);
+ if (!pd || attrs.isGeneric()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+ }
+ if (hasProperty)
+ *hasProperty = true;
+ return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs);
+}
+
+Value String::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty)
+{
+ String *that = static_cast<String *>(m);
+ if (index < that->_text.length()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromString(ctx, that->toQString().mid(index, 1));
+ }
+ PropertyAttributes attrs;
+ Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(index, &attrs);
+ if (!pd || attrs.isGeneric()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+ }
+ if (hasProperty)
+ *hasProperty = true;
+ return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs);
+}
+
+void String::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value)
+{
+ String *that = static_cast<String *>(m);
+ Object *o = ctx->engine->newStringObject(ctx, Value::fromString(that));
+ o->put(ctx, name, value);
+}
+
+void String::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value)
+{
+ String *that = static_cast<String *>(m);
+ Object *o = ctx->engine->newStringObject(ctx, Value::fromString(that));
+ o->putIndexed(ctx, index, value);
+}
+
+PropertyAttributes String::query(Managed *m, ExecutionContext *ctx, String *name)
+{
+ return Attr_Invalid;
+}
+
+PropertyAttributes String::queryIndexed(Managed *m, ExecutionContext *ctx, uint index)
+{
+ String *that = static_cast<String *>(m);
+ return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid;
+}
+
+bool String::deleteProperty(Managed *m, ExecutionContext *ctx, String *name)
+{
+ return false;
+}
+
+bool String::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index)
+{
+ return false;
+}
+
+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(this);
+ uint l = (uint)d;
+ if (d == l)
+ return l;
+ *ok = false;
+ return UINT_MAX;
+}
+
+void String::makeIdentifierImpl(const ExecutionContext *ctx)
+{
+ ctx->engine->identifierCache->toIdentifier(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;
+}
+
+}
+}
diff --git a/src/qml/qml/v4vm/qv4string.h b/src/qml/qml/v4vm/qv4string.h
new file mode 100644
index 0000000000..f4eeb7d0a8
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4string.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct ExecutionEngine;
+
+struct String : public Managed {
+ enum StringType {
+ StringType_Unknown,
+ StringType_Regular,
+ StringType_UInt,
+ StringType_ArrayIndex
+ };
+
+ String(const QString &text)
+ : _text(text), stringHash(UINT_MAX), identifier(UINT_MAX)
+ { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; }
+ ~String() { _data = 0; }
+
+ inline bool isEqualTo(const String *other) const {
+ if (this == other)
+ return true;
+ if (hashValue() != other->hashValue())
+ return false;
+ if (identifier != UINT_MAX && 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 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(const ExecutionContext *ctx) {
+ if (identifier != UINT_MAX)
+ return;
+ makeIdentifierImpl(ctx);
+ }
+
+ void makeIdentifierImpl(const ExecutionContext *ctx);
+
+ void createHashValue() const;
+ static uint createHashValue(const QChar *ch, int length);
+
+ QString _text;
+ mutable uint stringHash;
+ mutable uint identifier;
+
+protected:
+ static void destroy(Managed *);
+ static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty);
+ static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty);
+ static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value);
+ static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value);
+ static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name);
+ static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index);
+ static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name);
+ static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index);
+
+ static const ManagedVTable static_vtbl;
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/qv4stringobject.cpp b/src/qml/qml/v4vm/qv4stringobject.cpp
new file mode 100644
index 0000000000..42b9b422ec
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4stringobject.cpp
@@ -0,0 +1,726 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4regexpobject.h"
+#include "qv4objectproto.h"
+#include "qv4mm.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 QQmlJS::VM;
+
+DEFINE_MANAGED_VTABLE(StringObject);
+
+StringObject::StringObject(ExecutionContext *ctx, const Value &value)
+ : Object(ctx->engine), value(value)
+{
+ vtbl = &static_vtbl;
+ type = Type_StringObject;
+
+ tmpProperty.value = Value::undefinedValue();
+
+ assert(value.isString());
+ defineReadonlyProperty(ctx->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;
+}
+
+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)
+{
+ vtbl = &static_vtbl;
+}
+
+Value StringCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc)
+{
+ Value value;
+ if (argc)
+ value = Value::fromString(argv[0].toString(ctx));
+ else
+ value = Value::fromString(ctx, QString());
+ return Value::fromObject(ctx->engine->newStringObject(ctx, value));
+}
+
+Value StringCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc)
+{
+ Value value;
+ if (argc)
+ value = Value::fromString(argv[0].toString(parentCtx));
+ else
+ value = Value::fromString(parentCtx, QString());
+ return value;
+}
+
+void StringPrototype::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("fromCharCode"), method_fromCharCode, 1);
+
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical
+ defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1);
+ defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1);
+ defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
+ defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1);
+ defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1);
+ defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2);
+ defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1);
+ defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2);
+ defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2);
+ defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2);
+ defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase);
+ defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase);
+ defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
+ defineDefaultProperty(ctx, 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 (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.asRegExpObject();
+ if (!rx)
+ rx = context->engine->regExpCtor.asFunctionObject()->construct(context, &regexp, 1).asRegExpObject();
+
+ 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, context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject();
+
+ Value arg = Value::fromString(s);
+ if (!global)
+ return exec->call(context, Value::fromObject(rx), &arg, 1);
+
+ String *lastIndex = context->engine->newString(QStringLiteral("lastIndex"));
+ rx->put(context, lastIndex, Value::fromInt32(0));
+ ArrayObject *a = context->engine->newArrayObject(context);
+
+ double previousLastIndex = 0;
+ uint n = 0;
+ while (1) {
+ Value result = exec->call(context, Value::fromObject(rx), &arg, 1);
+ if (result.isNull())
+ break;
+ assert(result.isObject());
+ double thisIndex = rx->get(context, lastIndex, 0).toInteger();
+ if (previousLastIndex == thisIndex) {
+ previousLastIndex = thisIndex + 1;
+ rx->put(context, lastIndex, Value::fromDouble(previousLastIndex));
+ } else {
+ previousLastIndex = thisIndex;
+ }
+ Value matchStr = result.objectValue()->getIndexed(context, 0, (bool *)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.asRegExpObject();
+ 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(ctx, 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.asRegExpObject();
+ if (!regExp) {
+ regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, &regExpValue, 1);
+ regExp = regExpValue.asRegExpObject();
+ }
+ 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(ctx);
+ 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.asRegExpObject()) {
+ if (re->value->pattern().isEmpty()) {
+ re = 0;
+ separatorValue = Value::fromString(ctx, QString());
+ }
+ }
+
+ if (RegExpObject* re = separatorValue.asRegExpObject()) {
+ 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 (isnan(start) || start < 0)
+ start = 0;
+
+ if (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/v4vm/qv4stringobject.h b/src/qml/qml/v4vm/qv4stringobject.h
new file mode 100644
index 0000000000..ded26c501b
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4stringobject.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+#include "qv4functionobject.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct StringObject: Object {
+ Value value;
+ mutable Property tmpProperty;
+ StringObject(ExecutionContext *ctx, const Value &value);
+
+ Property *getIndex(uint index) const;
+
+protected:
+ static const ManagedVTable static_vtbl;
+ static void markObjects(Managed *that);
+};
+
+struct StringCtor: FunctionObject
+{
+ StringCtor(ExecutionContext *scope);
+
+ static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc);
+ static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int);
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+struct StringPrototype: StringObject
+{
+ StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {}
+ void init(ExecutionContext *ctx, 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);
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/qml/v4vm/qv4syntaxchecker.cpp b/src/qml/qml/v4vm/qv4syntaxchecker.cpp
new file mode 100644
index 0000000000..fcda486af2
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4syntaxchecker.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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"
+
+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;
+}
diff --git a/src/qml/qml/v4vm/qv4syntaxchecker_p.h b/src/qml/qml/v4vm/qv4syntaxchecker_p.h
new file mode 100644
index 0000000000..38e123762e
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4syntaxchecker_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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>
+
+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
+
+#endif // QV4SYNTAXCHECKER_P_H
diff --git a/src/qml/qml/v4vm/qv4unwindhelper.cpp b/src/qml/qml/v4vm/qv4unwindhelper.cpp
new file mode 100644
index 0000000000..8a50b0bb53
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4unwindhelper.cpp
@@ -0,0 +1,37 @@
+#include <qv4unwindhelper.h>
+
+#include <wtf/Platform.h>
+
+#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X))
+# define USE_DW2_HELPER
+#elif CPU(X86) && OS(LINUX)
+# define USE_DW2_HELPER
+#elif CPU(ARM) && OS(LINUX)
+# 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
+
+#ifdef USE_NULL_HELPER
+using namespace QQmlJS::VM;
+void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);}
+void UnwindHelper::registerFunctions(QVector<Function *> functions) {Q_UNUSED(functions);}
+void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);}
+void UnwindHelper::deregisterFunctions(QVector<Function *> functions) {Q_UNUSED(functions);}
+#endif // USE_NULL_HELPER
+
diff --git a/src/qml/qml/v4vm/qv4unwindhelper.h b/src/qml/qml/v4vm/qv4unwindhelper.h
new file mode 100644
index 0000000000..9f6462d644
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4unwindhelper.h
@@ -0,0 +1,27 @@
+#ifndef QV4UNWINDHELPER_H
+#define QV4UNWINDHELPER_H
+
+#include <QtCore/QVector>
+
+namespace QQmlJS {
+namespace VM {
+
+struct Function;
+
+class UnwindHelper
+{
+public:
+ static void registerFunction(Function *function);
+ static void registerFunctions(QVector<Function *> functions);
+ static void deregisterFunction(Function *function);
+ static void deregisterFunctions(QVector<Function *> functions);
+#ifdef Q_PROCESSOR_ARM
+ static int unwindInfoSize();
+ static void writeARMUnwindInfo(void *codeAddr, int codeSize);
+#endif
+};
+
+} // VM namespace
+} // QQmlJS namespace
+
+#endif // QV4UNWINDHELPER_H
diff --git a/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h b/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h
new file mode 100644
index 0000000000..6938fa5189
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h
@@ -0,0 +1,176 @@
+#ifndef QV4UNWINDHELPER_PDW2_H
+#define QV4UNWINDHELPER_PDW2_H
+
+#include <qv4unwindhelper.h>
+#include <qv4functionobject.h>
+#include <wtf/Platform.h>
+
+#include <QMap>
+#include <QMutex>
+
+#define __USE_GNU
+#include <dlfcn.h>
+
+#if USE(LIBUNWIND_DEBUG)
+#include <libunwind.h>
+#endif
+#include <execinfo.h>
+
+namespace QQmlJS {
+namespace VM {
+
+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(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(QVector<Function *> functions)
+{
+ QMutexLocker locker(&functionProtector);
+ foreach (Function *f, functions)
+ allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
+}
+
+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
+}
+
+}
+}
+
+extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount)
+{
+ typedef void *(*Old_Unwind_Find_exidx)(void*, int*);
+ static Old_Unwind_Find_exidx oldFunction = 0;
+ static ptrdiff_t *exidx = (ptrdiff_t*)malloc(2 * sizeof(uintptr_t));
+ if (!oldFunction)
+ oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx");
+
+ {
+ QMutexLocker locker(&QQmlJS::VM::functionProtector);
+ QQmlJS::VM::Function *function = QQmlJS::VM::lookupFunction(pc);
+ if (function) {
+ *entryCount = 1;
+ void * codeStart = QQmlJS::VM::removeThumbBit(function->codeRef.code().executableAddress());
+ // At the end of the function we store our synthetic exception table entry.
+ return (char *)codeStart + function->codeSize;
+ }
+ }
+
+ return oldFunction(pc, entryCount);
+}
+
+#endif // QV4UNWINDHELPER_PDW2_H
diff --git a/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h
new file mode 100644
index 0000000000..10c99f539c
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h
@@ -0,0 +1,189 @@
+#ifndef QV4UNWINDHELPER_PDW2_H
+#define QV4UNWINDHELPER_PDW2_H
+
+#include <qv4unwindhelper.h>
+#include <qv4functionobject.h>
+#include <wtf/Platform.h>
+
+#include <QMap>
+#include <QMutex>
+
+#define __USE_GNU
+#include <dlfcn.h>
+
+namespace QQmlJS {
+namespace VM {
+
+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) && OS(LINUX)
+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
+} // anonymous namespace
+
+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>((*it)->code);
+ if (key < codeStart || key >= codeStart + (*it)->codeSize)
+ return 0;
+ return *it;
+}
+
+namespace {
+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
+
+static void ensureUnwindInfo(Function *f)
+{
+ if (!f->unwindInfo.isEmpty())
+ 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 *>(f->codeRef.code().executableAddress()) - static_cast<char *>(0);
+ writeIntPtrValue(cie_and_fde + initial_location_offset, ptr);
+
+ writeIntPtrValue(cie_and_fde + address_range_offset, f->codeSize);
+
+ f->unwindInfo = info;
+}
+
+#if defined(Q_OS_DARWIN)
+extern "C" void __register_frame(void *fde);
+extern "C" void __deregister_frame(void *fde);
+#endif
+
+static void registerFunctionUnlocked(Function *f)
+{
+ allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
+#if defined(Q_OS_DARWIN)
+ ensureUnwindInfo(f);
+ __register_frame(f->unwindInfo.data() + fde_offset);
+#endif
+}
+
+static void deregisterFunctionUnlocked(Function *f)
+{
+ allFunctions.remove(reinterpret_cast<quintptr>(f->code));
+#if defined(Q_OS_DARWIN)
+ if (!f->unwindInfo.isEmpty())
+ __deregister_frame(f->unwindInfo.data() + fde_offset);
+#endif
+}
+
+void UnwindHelper::registerFunction(Function *function)
+{
+ QMutexLocker locker(&functionProtector);
+ registerFunctionUnlocked(function);
+}
+
+void UnwindHelper::registerFunctions(QVector<Function *> functions)
+{
+ QMutexLocker locker(&functionProtector);
+ foreach (Function *f, functions)
+ registerFunctionUnlocked(f);
+}
+
+void UnwindHelper::deregisterFunction(Function *function)
+{
+ QMutexLocker locker(&functionProtector);
+ deregisterFunctionUnlocked(function);
+}
+
+void UnwindHelper::deregisterFunctions(QVector<Function *> functions)
+{
+ QMutexLocker locker(&functionProtector);
+ foreach (Function *f, functions)
+ deregisterFunctionUnlocked(f);
+}
+
+} // VM namespace
+} // QQmlJS namespace
+
+#if defined(Q_OS_LINUX)
+extern "C" {
+
+struct bases
+{
+ void *tbase;
+ void *dbase;
+ void *func;
+};
+
+Q_V4_EXPORT void *_Unwind_Find_FDE(void *pc, struct bases *bases)
+{
+ typedef void *(*Old_Unwind_Find_FDE)(void *pc, struct bases *bases);
+ static Old_Unwind_Find_FDE oldFunction = 0;
+ if (!oldFunction)
+ oldFunction = (Old_Unwind_Find_FDE)dlsym(RTLD_NEXT, "_Unwind_Find_FDE");
+
+ {
+ QMutexLocker locker(&QQmlJS::VM::functionProtector);
+ QQmlJS::VM::Function *function = QQmlJS::VM::lookupFunction(pc);
+ if (function) {
+ bases->tbase = 0;
+ bases->dbase = 0;
+ bases->func = reinterpret_cast<void*>(function->code);
+ QQmlJS::VM::ensureUnwindInfo(function);
+ return function->unwindInfo.data() + QQmlJS::VM::fde_offset;
+ }
+ }
+
+ return oldFunction(pc, bases);
+}
+
+}
+#endif
+
+#endif // QV4UNWINDHELPER_PDW2_H
diff --git a/src/qml/qml/v4vm/qv4util.h b/src/qml/qml/v4vm/qv4util.h
new file mode 100644
index 0000000000..c43d85eca7
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4util.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+
+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>&);
+};
+
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif // QV4UTIL_H
diff --git a/src/qml/qml/v4vm/qv4v8.cpp b/src/qml/qml/v4vm/qv4v8.cpp
new file mode 100644
index 0000000000..d81f23f30f
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4v8.cpp
@@ -0,0 +1,2141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 "qv4v8.h"
+
+#include "qv4engine.h"
+#include "qv4runtime.h"
+#include "qv4mm.h"
+#include "qv4managed.h"
+#include "qv4functionobject.h"
+#include "qv4value.h"
+#include "qv4isel_masm_p.h"
+#include "qv4globalobject.h"
+#include "qv4regexpobject.h"
+#include "qv4dateobject.h"
+#include "qv4numberobject.h"
+#include "qv4booleanobject.h"
+#include "qv4stringobject.h"
+#include "qv4objectproto.h"
+#include <QThreadStorage>
+
+using namespace QQmlJS;
+using namespace QQmlJS::VM;
+
+namespace v8 {
+
+#define currentEngine() Isolate::GetCurrent()->GetCurrentContext()->GetEngine()
+
+#define Q_D(obj) QQmlJS::VM::Value *d = reinterpret_cast<QQmlJS::VM::Value*>(obj)
+
+#define ValuePtr(obj) reinterpret_cast<QQmlJS::VM::Value*>(obj)
+#define ConstValuePtr(obj) reinterpret_cast<const QQmlJS::VM::Value*>(obj)
+
+void *gcProtect(void *handle)
+{
+ Q_D(handle);
+ if (VM::Managed *m = d->asManaged()) {
+ currentEngine()->memoryManager->protect(m);
+ return currentEngine()->memoryManager;
+ }
+}
+
+void gcProtect(void *memoryManager, void *handle)
+{
+ Q_D(handle);
+ if (VM::Managed *m = d->asManaged())
+ if (memoryManager)
+ static_cast<VM::MemoryManager *>(memoryManager)->protect(m);
+}
+
+void gcUnprotect(void *memoryManager, void *handle)
+{
+ Q_D(handle);
+ if (VM::Managed *m = d->asManaged())
+ if (memoryManager)
+ static_cast<VM::MemoryManager *>(memoryManager)->unprotect(m);
+}
+
+struct V8AccessorGetter: FunctionObject {
+ AccessorGetter getter;
+ Persistent<Value> data;
+ Persistent<String> name;
+
+ V8AccessorGetter(ExecutionContext *scope, const Handle<String> &name, const AccessorGetter &getter, Handle<Value> data)
+ : FunctionObject(scope)
+ {
+ vtbl = &static_vtbl;
+ this->getter = getter;
+ this->data = Persistent<Value>::New(data);
+ this->name = Persistent<String>::New(name);
+ }
+
+ using Object::construct;
+
+ static VM::Value call(Managed *that, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc)
+ {
+ V8AccessorGetter *getter = static_cast<V8AccessorGetter*>(that);
+ AccessorInfo info(thisObject, getter->data);
+ VM::Value result = VM::Value::undefinedValue();
+ try {
+ result = getter->getter(Local<String>::New(getter->name), info)->vmValue();
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(context);
+ }
+ return result;
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+DEFINE_MANAGED_VTABLE(V8AccessorGetter);
+
+struct V8AccessorSetter: FunctionObject {
+ AccessorSetter setter;
+ Persistent<Value> data;
+ Persistent<String> name;
+
+ V8AccessorSetter(ExecutionContext *scope, const Handle<String> &name, const AccessorSetter &setter, Handle<Value> data)
+ : FunctionObject(scope)
+ {
+ vtbl = &static_vtbl;
+ this->setter = setter;
+ this->data = Persistent<Value>::New(data);
+ this->name = Persistent<String>::New(name);
+ }
+
+ using Object::construct;
+
+ static VM::Value call(Managed *that, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc)
+ {
+ if (!argc)
+ return VM::Value::undefinedValue();
+ V8AccessorSetter *setter = static_cast<V8AccessorSetter*>(that);
+ AccessorInfo info(thisObject, setter->data);
+ try {
+ setter->setter(Local<String>::New(setter->name), Local<Value>::New(Value::fromVmValue(args[0])), info);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(context);
+ }
+ return VM::Value::undefinedValue();
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+};
+
+DEFINE_MANAGED_VTABLE(V8AccessorSetter);
+
+ScriptOrigin::ScriptOrigin(Handle<Value> resource_name, Handle<Integer> resource_line_offset, Handle<Integer> resource_column_offset)
+{
+ m_fileName = resource_name->ToString()->asQString();
+ m_lineNumber = resource_line_offset->ToInt32()->Value();
+ m_columnNumber = resource_column_offset->ToInt32()->Value();
+}
+
+Handle<Value> ScriptOrigin::ResourceName() const
+{
+ return Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_fileName));
+}
+
+Handle<Integer> ScriptOrigin::ResourceLineOffset() const
+{
+ return Integer::New(m_lineNumber);
+}
+
+Handle<Integer> ScriptOrigin::ResourceColumnOffset() const
+{
+ return Integer::New(m_columnNumber);
+}
+
+
+Local<Script> Script::New(Handle<String> source,
+ ScriptOrigin* origin,
+ ScriptData* pre_data,
+ Handle<String> script_data,
+ CompileFlags flags)
+{
+ Script *s = new Script;
+ s->m_script = source->ToString()->asQString();
+ if (origin)
+ s->m_origin = *origin;
+ s->m_flags = flags;
+ s->m_context = Handle<Context>();
+ return Local<Script>::New(Handle<Script>(s));
+}
+
+
+Local<Script> Script::New(Handle<String> source,
+ Handle<Value> file_name,
+ CompileFlags flags)
+{
+ ScriptOrigin origin(file_name);
+ return New(source, &origin, 0, Handle<String>(), flags);
+}
+
+Local<Script> Script::Compile(Handle<String> source, ScriptOrigin *origin, ScriptData *pre_data, Handle<String> script_data, Script::CompileFlags flags)
+{
+ Script *s = new Script;
+ s->m_script = source->ToString()->asQString();
+ if (origin)
+ s->m_origin = *origin;
+ s->m_flags = flags;
+ s->m_context = Context::GetCurrent();
+ return Local<Script>::New(Handle<Script>(s));
+}
+
+Local<Script> Script::Compile(Handle<String> source,
+ Handle<Value> file_name,
+ Handle<String> script_data,
+ CompileFlags flags)
+{
+ ScriptOrigin origin(file_name);
+ return Compile(source, &origin, 0, script_data, flags);
+}
+
+Local<Value> Script::Run()
+{
+ Handle<Context> context = m_context;
+ if (context.IsEmpty())
+ context = Context::GetCurrent();
+ ASSERT(context.get());
+ VM::ExecutionEngine *engine = context->GetEngine();
+ VM::ExecutionContext *ctx = engine->current;
+
+ VM::Value result = VM::Value::undefinedValue();
+ try {
+ QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(engine->rootContext, m_origin.m_fileName, m_script, QQmlJS::Codegen::EvalCode,
+ /*strictMode =*/ false, /*inheritContext =*/ false);
+ if (!f)
+ __qmljs_throw(engine->current, VM::Value::fromObject(engine->newSyntaxErrorObject(engine->current, 0)));
+
+ result = context->GetEngine()->run(f);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+
+ return Local<Value>::New(Value::fromVmValue(result));
+}
+
+Local<Value> Script::Run(Handle<Object> qml)
+{
+ Handle<Context> context = m_context;
+ if (context.IsEmpty())
+ context = Context::GetCurrent();
+ ASSERT(context.get());
+ VM::ExecutionEngine *engine = context->GetEngine();
+ VM::ExecutionContext *ctx = engine->current;
+
+ VM::Value result = VM::Value::undefinedValue();
+
+ try {
+
+ VM::EvalFunction *eval = new (engine->memoryManager) VM::EvalFunction(engine->rootContext, qml->vmValue().asObject());
+
+ VM::Value arg = VM::Value::fromString(engine->current, m_script);
+
+ result = eval->evalCall(engine->current, VM::Value::undefinedValue(), &arg, 1, /*directCall*/ false);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+ return Local<Value>::New(Value::fromVmValue(result));
+}
+
+Local<Value> Script::Id()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void Script::SetData(Handle<String> data)
+{
+ Q_UNIMPLEMENTED();
+}
+
+
+Local<String> Message::Get() const
+{
+ return Local<String>::New(Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_message)));
+}
+
+Handle<Value> Message::GetScriptResourceName() const
+{
+ return Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_resourceName));
+}
+
+int Message::GetLineNumber() const
+{
+ return m_lineNumber;
+}
+
+
+Local<StackFrame> StackTrace::GetFrame(uint32_t index) const
+{
+ if (index >= (uint)frames.size())
+ return Local<StackFrame>();
+ return frames.at(index);
+}
+
+int StackTrace::GetFrameCount() const
+{
+ return frames.size();
+}
+
+Local<Array> StackTrace::AsArray()
+{
+ Q_UNIMPLEMENTED();
+ return Local<Array>();
+}
+
+Local<StackTrace> StackTrace::CurrentStackTrace(int frame_limit, StackTrace::StackTraceOptions options)
+{
+ StackTrace *trace = new StackTrace;
+ VM::ExecutionEngine *engine = currentEngine();
+ VM::ExecutionContext *current = engine->current;
+ while (current && frame_limit) {
+ if (CallContext *c = current->asCallContext()) {
+ StackFrame *frame = new StackFrame(Value::fromVmValue(VM::Value::fromString(engine->id_null)),
+ Value::fromVmValue(VM::Value::fromString(c->function->name)),
+ 0, 0);
+ trace->frames.append(frame);
+ --frame_limit;
+ }
+ current = current->parent;
+ }
+
+ return Local<StackTrace>::New(Handle<StackTrace>(trace));
+}
+
+
+int StackFrame::GetLineNumber() const
+{
+ return m_lineNumber;
+}
+
+int StackFrame::GetColumn() const
+{
+ return m_columnNumber;
+}
+
+Local<String> StackFrame::GetScriptName() const
+{
+ return Local<String>::New(m_scriptName);
+}
+
+Local<String> StackFrame::GetScriptNameOrSourceURL() const
+{
+ return Local<String>::New(m_scriptName);
+}
+
+Local<String> StackFrame::GetFunctionName() const
+{
+ return Local<String>::New(m_functionName);
+}
+
+StackFrame::StackFrame(Handle<String> script, Handle<String> function, int line, int column)
+ : m_lineNumber(line)
+ , m_columnNumber(column)
+{
+ m_scriptName = Persistent<String>::New(script);
+ m_functionName = Persistent<String>::New(function);
+}
+
+
+bool Value::IsUndefined() const
+{
+ return ConstValuePtr(this)->isUndefined();
+}
+
+bool Value::IsNull() const {
+ return ConstValuePtr(this)->isNull();
+}
+
+bool Value::IsTrue() const
+{
+ return ConstValuePtr(this)->isBoolean() && ConstValuePtr(this)->booleanValue();
+}
+
+bool Value::IsFalse() const
+{
+ return !IsTrue();
+}
+
+bool Value::IsString() const
+{
+ return ConstValuePtr(this)->isString();
+}
+
+bool Value::IsFunction() const
+{
+ return ConstValuePtr(this)->asFunctionObject();
+}
+
+bool Value::IsArray() const
+{
+ return ConstValuePtr(this)->asArrayObject();
+}
+
+bool Value::IsObject() const
+{
+ return ConstValuePtr(this)->isObject();
+}
+
+bool Value::IsBoolean() const
+{
+ return ConstValuePtr(this)->isBoolean();
+}
+
+bool Value::IsNumber() const
+{
+ return ConstValuePtr(this)->isNumber();
+}
+
+bool Value::IsExternal() const
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+bool Value::IsInt32() const
+{
+ return ConstValuePtr(this)->isInteger();
+}
+
+bool Value::IsUint32() const
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+bool Value::IsDate() const
+{
+ return ConstValuePtr(this)->asDateObject();
+}
+
+bool Value::IsBooleanObject() const
+{
+ return ConstValuePtr(this)->asBooleanObject();
+}
+
+bool Value::IsNumberObject() const
+{
+ return ConstValuePtr(this)->asNumberObject();
+}
+
+bool Value::IsStringObject() const
+{
+ return ConstValuePtr(this)->asStringObject();
+}
+
+bool Value::IsRegExp() const
+{
+ return ConstValuePtr(this)->asRegExpObject();
+}
+
+bool Value::IsError() const
+{
+ return ConstValuePtr(this)->asErrorObject();
+}
+
+Local<Boolean> Value::ToBoolean() const
+{
+ return Local<Boolean>::New(Value::fromVmValue(VM::Value::fromBoolean(ConstValuePtr(this)->toBoolean())));
+}
+
+Local<Number> Value::ToNumber() const
+{
+ return Local<Number>::New(Value::fromVmValue(VM::Value::fromDouble(ConstValuePtr(this)->toNumber())));
+}
+
+Local<String> Value::ToString() const
+{
+ return Local<String>::New(Value::fromVmValue(VM::Value::fromString(ConstValuePtr(this)->toString(currentEngine()->current))));
+}
+
+Local<Object> Value::ToObject() const
+{
+ return Local<Object>::New(Value::fromVmValue(QQmlJS::VM::Value::fromObject(ConstValuePtr(this)->toObject(currentEngine()->current))));
+}
+
+Local<Integer> Value::ToInteger() const
+{
+ return Local<Integer>::New(Value::fromVmValue(QQmlJS::VM::Value::fromDouble(ConstValuePtr(this)->toInteger())));
+}
+
+Local<Uint32> Value::ToUint32() const
+{
+ return Local<Uint32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromUInt32(ConstValuePtr(this)->toUInt32())));
+}
+
+Local<Int32> Value::ToInt32() const
+{
+ return Local<Int32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromInt32(ConstValuePtr(this)->toInt32())));
+}
+
+Local<Uint32> Value::ToArrayIndex() const
+{
+ return Local<Uint32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromUInt32(ConstValuePtr(this)->asArrayIndex())));
+}
+
+bool Value::BooleanValue() const
+{
+ return ConstValuePtr(this)->toBoolean();
+}
+
+double Value::NumberValue() const
+{
+ return ConstValuePtr(this)->asDouble();
+}
+
+int64_t Value::IntegerValue() const
+{
+ return (int64_t)ConstValuePtr(this)->toInteger();
+}
+
+uint32_t Value::Uint32Value() const
+{
+ return ConstValuePtr(this)->toUInt32();
+}
+
+int32_t Value::Int32Value() const
+{
+ return ConstValuePtr(this)->toInt32();
+}
+
+bool Value::Equals(Handle<Value> that) const
+{
+ return __qmljs_equal(*ConstValuePtr(this), *ConstValuePtr(&that));
+}
+
+bool Value::StrictEquals(Handle<Value> that) const
+{
+ return __qmljs_strict_equal(*ConstValuePtr(this), *ConstValuePtr(&that));
+}
+
+VM::Value Value::vmValue() const
+{
+ return *ConstValuePtr(this);
+}
+
+Handle<Value> Value::fromVmValue(const VM::Value &vmValue)
+{
+ Handle<Value> res;
+ res.val = vmValue.val;
+ return res;
+}
+
+
+bool Boolean::Value() const
+{
+ return BooleanValue();
+}
+
+Handle<Boolean> Boolean::New(bool value)
+{
+ return Value::fromVmValue(VM::Value::fromBoolean(value));
+}
+
+
+int String::Length() const
+{
+ return asVMString()->toQString().length();
+}
+
+uint32_t String::Hash() const
+{
+ return asVMString()->hashValue();
+}
+
+
+String::CompleteHashData String::CompleteHash() const
+{
+ VM::String *s = asVMString();
+ CompleteHashData data;
+ data.hash = s->hashValue();
+ data.length = s->toQString().length();
+ data.symbol_id = s->identifier;
+ return data;
+}
+
+uint32_t String::ComputeHash(uint16_t *string, int length)
+{
+ return VM::String::createHashValue(reinterpret_cast<const QChar *>(string), length);
+}
+
+uint32_t String::ComputeHash(char *string, int length)
+{
+ // ### unefficient
+ QString s = QString::fromLatin1((char *)string, length);
+ return VM::String::createHashValue(s.constData(), s.length());
+}
+
+bool String::Equals(uint16_t *str, int length)
+{
+ return asQString() == QString(reinterpret_cast<QChar*>(str), length);
+}
+
+bool String::Equals(char *str, int length)
+{
+ return asQString() == QString::fromLatin1(str, length);
+}
+
+uint16_t String::GetCharacter(int index)
+{
+ return asQString().at(index).unicode();
+}
+
+int String::Write(uint16_t *buffer, int start, int length, int options) const
+{
+ if (length < 0)
+ length = asQString().length();
+ if (length == 0)
+ return 0;
+ if (asQString().length() < length)
+ length = asQString().length();
+ // ### do we use options?
+ memcpy(buffer + start, asQString().constData(), length*sizeof(QChar));
+ return length;
+}
+
+v8::Local<String> String::Empty()
+{
+ return Local<String>::New(v8::Value::fromVmValue(VM::Value::fromString(currentEngine()->current, QString())));
+}
+
+bool String::IsExternal() const
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+String::ExternalStringResource *String::GetExternalStringResource() const
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+String *String::Cast(v8::Value *obj)
+{
+ return static_cast<String *>(obj);
+}
+
+
+Local<String> String::New(const char *data, int length)
+{
+ QQmlJS::VM::Value v = QQmlJS::VM::Value::fromString(currentEngine()->current, QString::fromLatin1(data, length));
+ return Local<String>::New(v8::Value::fromVmValue(v));
+}
+
+Local<String> String::New(const uint16_t *data, int length)
+{
+ QQmlJS::VM::Value v = QQmlJS::VM::Value::fromString(currentEngine()->current, QString((const QChar *)data, length));
+ return Local<String>::New(v8::Value::fromVmValue(v));
+}
+
+Local<String> String::NewSymbol(const char *data, int length)
+{
+ QString str = QString::fromLatin1(data, length);
+ VM::String *vmString = currentEngine()->newIdentifier(str);
+ return New(vmString);
+}
+
+Local<String> String::New(VM::String *s)
+{
+ return Local<String>::New(v8::Value::fromVmValue(VM::Value::fromString(s)));
+}
+
+Local<String> String::NewExternal(String::ExternalStringResource *resource)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+QString String::asQString() const
+{
+ return asVMString()->toQString();
+}
+
+VM::String *String::asVMString() const
+{
+ const VM::Value *v = ConstValuePtr(this);
+ ASSERT(v->isString());
+ return v->stringValue();
+}
+
+String::AsciiValue::AsciiValue(Handle<v8::Value> obj)
+{
+ str = obj->ToString()->asQString().toLatin1();
+}
+
+String::Value::Value(Handle<v8::Value> obj)
+{
+ str = obj->ToString()->asQString();
+}
+
+
+double Number::Value() const
+{
+ const VM::Value *v = ConstValuePtr(this);
+ assert(v->isNumber());
+ return v->asDouble();
+}
+
+Local<Number> Number::New(double value)
+{
+ return Local<Number>::New(Value::fromVmValue(VM::Value::fromDouble(value)));
+}
+
+Number *Number::Cast(v8::Value *obj)
+{
+ return static_cast<Number *>(obj);
+}
+
+Local<Integer> Integer::New(int32_t value)
+{
+ return Local<Integer>::New(Value::fromVmValue(VM::Value::fromInt32(value)));
+}
+
+Local<Integer> Integer::NewFromUnsigned(uint32_t value)
+{
+ return Local<Integer>::New(Value::fromVmValue(VM::Value::fromUInt32(value)));
+}
+
+Local<Integer> Integer::New(int32_t value, Isolate *)
+{
+ return New(value);
+}
+
+Local<Integer> Integer::NewFromUnsigned(uint32_t value, Isolate *)
+{
+ return NewFromUnsigned(value);
+}
+
+int64_t Integer::Value() const
+{
+ const VM::Value *v = ConstValuePtr(this);
+ assert(v->isNumber());
+ return (int64_t)v->asDouble();
+}
+
+Integer *Integer::Cast(v8::Value *obj)
+{
+ return static_cast<Integer *>(obj);
+}
+
+int32_t Int32::Value() const
+{
+ const VM::Value *v = ConstValuePtr(this);
+ assert(v->isInteger());
+ return v->int_32;
+}
+
+uint32_t Uint32::Value() const
+{
+ const VM::Value *v = ConstValuePtr(this);
+ assert(v->isNumber());
+ return v->toUInt32();
+}
+
+
+struct ExternalResourceWrapper : public QQmlJS::VM::Object::ExternalResource
+{
+ ExternalResourceWrapper(v8::Object::ExternalResource *wrapped)
+ {
+ this->wrapped = wrapped;
+ }
+
+ virtual ~ExternalResourceWrapper()
+ {
+ wrapped->Dispose();
+ }
+
+ v8::Object::ExternalResource *wrapped;
+};
+
+
+bool Object::Set(Handle<Value> key, Handle<Value> value, PropertyAttribute attribs)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current;
+ bool result = true;
+ try {
+ o->put(ctx, ValuePtr(&key)->toString(ctx), *ValuePtr(&value));
+ // ### attribs
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ result = false;
+ }
+ return result;
+}
+
+bool Object::Set(uint32_t index, Handle<Value> value)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current;
+ bool result = true;
+ try {
+ o->putIndexed(ctx, index, *ValuePtr(&value));
+ // ### attribs
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ result = false;
+ }
+ return result;
+}
+
+Local<Value> Object::Get(Handle<Value> key)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current;
+ QQmlJS::VM::Value prop = VM::Value::undefinedValue();
+ try {
+ prop = o->get(ctx, ValuePtr(&key)->toString(ctx));
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+ return Local<Value>::New(Value::fromVmValue(prop));
+}
+
+Local<Value> Object::Get(uint32_t key)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current;
+ QQmlJS::VM::Value prop = VM::Value::undefinedValue();
+ try {
+ prop = o->getIndexed(ctx, key);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+ return Local<Value>::New(Value::fromVmValue(prop));
+}
+
+bool Object::Has(Handle<String> key)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ return o->__hasProperty__(ValuePtr(&key)->asString());
+}
+
+bool Object::Delete(Handle<String> key)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ bool result = false;
+ ExecutionContext *ctx = currentEngine()->current;
+ try {
+ result = o->deleteProperty(ctx, ValuePtr(&key)->asString());
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+ return result;
+}
+
+bool Object::Has(uint32_t index)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ if (!o)
+ return false;
+ return o->__hasProperty__(index);
+}
+
+bool Object::Delete(uint32_t index)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ ExecutionContext *ctx = currentEngine()->current;
+ bool result = false;
+ try {
+ result = o->deleteIndexedProperty(ctx, index);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(ctx);
+ }
+ return result;
+}
+
+bool Object::SetAccessor(Handle<String> name, AccessorGetter getter, AccessorSetter setter, Handle<Value> data, AccessControl settings, PropertyAttribute attribute)
+{
+ VM::ExecutionEngine *engine = currentEngine();
+
+ VM::FunctionObject *wrappedGetter = 0;
+ if (getter) {
+ wrappedGetter = new (engine->memoryManager) V8AccessorGetter(engine->rootContext, name, getter, data);
+ }
+ VM::FunctionObject *wrappedSetter = 0;
+ if (setter) {
+ wrappedSetter = new (engine->memoryManager) V8AccessorSetter(engine->rootContext, name, setter, data);
+ }
+
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ PropertyAttributes attrs = Attr_Accessor;
+ attrs.setConfigurable(!(attribute & DontDelete));
+ attrs.setEnumerable(!(attribute & DontEnum));
+ VM::Property *pd = o->insertMember(name->asVMString(), attrs);
+ pd->setGetter(wrappedGetter);
+ pd->setSetter(wrappedSetter);
+ return true;
+}
+
+Local<Array> Object::GetPropertyNames()
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+
+ VM::ArrayObject *array = currentEngine()->newArrayObject(currentEngine()->current)->asArrayObject();
+ ObjectIterator it(currentEngine()->current, o, ObjectIterator::WithProtoChain|ObjectIterator::EnumberableOnly);
+ while (1) {
+ VM::Value v = it.nextPropertyNameAsString();
+ if (v.isNull())
+ break;
+ array->push_back(v);
+ }
+ return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(array)));
+}
+
+Local<Array> Object::GetOwnPropertyNames()
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ VM::Value arg = VM::Value::fromObject(o);
+ ArrayObject *array = currentEngine()->newArrayObject(currentEngine()->current)->asArrayObject();
+ ObjectIterator it(currentEngine()->current, o, ObjectIterator::EnumberableOnly);
+ while (1) {
+ VM::Value v = it.nextPropertyNameAsString();
+ if (v.isNull())
+ break;
+ array->push_back(v);
+ }
+ return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(array)));
+}
+
+Local<Value> Object::GetPrototype()
+{
+ Local<Value> result;
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ return Local<Value>::New(Value::fromVmValue(QQmlJS::VM::Value::fromObject(o->prototype)));
+}
+
+bool Object::SetPrototype(Handle<Value> prototype)
+{
+ QQmlJS::VM::Object *p = ConstValuePtr(&prototype)->asObject();
+ if (!p)
+ return false;
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ o->prototype = p;
+ return true;
+}
+
+Local<Value> Object::GetInternalField(int index)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void Object::SetInternalField(int index, Handle<Value> value)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void Object::SetExternalResource(Object::ExternalResource *resource)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ if (!o)
+ return;
+ o->externalResource = new ExternalResourceWrapper(resource);
+}
+
+Object::ExternalResource *Object::GetExternalResource()
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ if (!o || !o->externalResource)
+ return 0;
+ return static_cast<ExternalResourceWrapper*>(o->externalResource)->wrapped;
+}
+
+bool Object::HasOwnProperty(Handle<String> key)
+{
+ QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject();
+ assert(o);
+ QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current;
+ return o->__getOwnProperty__(ValuePtr(&key)->toString(ctx));
+}
+
+int Object::GetIdentityHash()
+{
+ return (quintptr)ConstValuePtr(this)->asObject() >> 2;
+}
+
+bool Object::SetHiddenValue(Handle<String> key, Handle<Value> value)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+Local<Value> Object::GetHiddenValue(Handle<String> key)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+Local<Object> Object::Clone()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+bool Object::IsCallable()
+{
+ return ConstValuePtr(this)->asFunctionObject();
+}
+
+Local<Value> Object::CallAsFunction(Handle<Object> recv, int argc, Handle<Value> argv[])
+{
+ VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ if (!f)
+ return Local<Value>();
+ VM::Value retval = f->call(currentEngine()->current, recv->vmValue(),
+ reinterpret_cast<QQmlJS::VM::Value*>(argv),
+ argc);
+ return Local<Value>::New(Value::fromVmValue(retval));
+}
+
+Local<Value> Object::CallAsConstructor(int argc, Handle<Value> argv[])
+{
+ VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ if (!f)
+ return Local<Value>();
+ VM::Value retval = f->construct(currentEngine()->current,
+ reinterpret_cast<QQmlJS::VM::Value*>(argv),
+ argc);
+ return Local<Value>::New(Value::fromVmValue(retval));
+}
+
+Local<Object> Object::New()
+{
+ VM::Object *o = currentEngine()->newObject();
+ return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Object *Object::Cast(Value *obj)
+{
+ return static_cast<Object *>(obj);
+}
+
+
+uint32_t Array::Length() const
+{
+ VM::ArrayObject *a = ConstValuePtr(this)->asArrayObject();
+ if (!a)
+ return 0;
+ return a->arrayLength();
+}
+
+Local<Array> Array::New(int length)
+{
+ VM::ArrayObject *a = currentEngine()->newArrayObject(currentEngine()->current);
+ if (length < 0x1000)
+ a->arrayReserve(length);
+
+ return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(a)));
+}
+
+Array *Array::Cast(Value *obj)
+{
+ return static_cast<Array *>(obj);
+}
+
+
+Local<Object> Function::NewInstance() const
+{
+ VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ assert(f);
+ VM::ExecutionContext *context = currentEngine()->current;
+ QQmlJS::VM::Value result = VM::Value::undefinedValue();
+ try {
+ result = f->construct(context, 0, 0);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(context);
+ }
+ return Local<Object>::New(Value::fromVmValue(result));
+}
+
+Local<Object> Function::NewInstance(int argc, Handle<Value> argv[]) const
+{
+ VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ assert(f);
+ VM::ExecutionContext *context = currentEngine()->current;
+ QQmlJS::VM::Value result = VM::Value::undefinedValue();
+ try {
+ result = f->construct(context, reinterpret_cast<QQmlJS::VM::Value*>(argv), argc);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(context);
+ }
+ return Local<Object>::New(Value::fromVmValue(result));
+}
+
+Local<Value> Function::Call(Handle<Object> thisObj, int argc, Handle<Value> argv[])
+{
+ QQmlJS::VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ if (!f)
+ return Local<Value>();
+ VM::ExecutionContext *context = currentEngine()->current;
+ QQmlJS::VM::Value result = VM::Value::undefinedValue();
+ try {
+ result = f->call(context, *ConstValuePtr(&thisObj),
+ reinterpret_cast<QQmlJS::VM::Value*>(argv), argc);
+ } catch (VM::Exception &e) {
+ Isolate::GetCurrent()->setException(e.value());
+ e.accept(context);
+ }
+ return Local<Value>::New(Value::fromVmValue(result));
+}
+
+Handle<Value> Function::GetName() const
+{
+ QQmlJS::VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject();
+ if (!f)
+ return Handle<Value>();
+ return Value::fromVmValue(VM::Value::fromString(f->name));
+}
+
+ScriptOrigin Function::GetScriptOrigin() const
+{
+ Q_UNIMPLEMENTED();
+ return ScriptOrigin();
+}
+
+Function *Function::Cast(Value *obj)
+{
+ return static_cast<Function *>(obj);
+}
+
+
+Local<Value> Date::New(double time)
+{
+ VM::Object *o = currentEngine()->newDateObject(VM::Value::fromDouble(time));
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+double Date::NumberValue() const
+{
+ DateObject *d = ConstValuePtr(this)->asDateObject();
+ assert(d);
+ return d->value.doubleValue();
+}
+
+Date *Date::Cast(Value *obj)
+{
+ return static_cast<Date *>(obj);
+}
+
+void Date::DateTimeConfigurationChangeNotification()
+{
+ Q_UNIMPLEMENTED();
+}
+
+
+Local<Value> NumberObject::New(double value)
+{
+ VM::Object *o = currentEngine()->newNumberObject(VM::Value::fromDouble(value));
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+double NumberObject::NumberValue() const
+{
+ VM::NumberObject *n = ConstValuePtr(this)->asNumberObject();
+ assert(n);
+ return n->value.doubleValue();
+}
+
+NumberObject *NumberObject::Cast(Value *obj)
+{
+ return static_cast<NumberObject *>(obj);
+}
+
+Local<Value> BooleanObject::New(bool value)
+{
+ VM::Object *o = currentEngine()->newBooleanObject(VM::Value::fromBoolean(value));
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+bool BooleanObject::BooleanValue() const
+{
+ VM::BooleanObject *b = ConstValuePtr(this)->asBooleanObject();
+ assert(b);
+ return b->value.booleanValue();
+}
+
+BooleanObject *BooleanObject::Cast(Value *obj)
+{
+ return static_cast<BooleanObject *>(obj);
+}
+
+Local<Value> StringObject::New(Handle<String> value)
+{
+ VM::Object *o = currentEngine()->newStringObject(currentEngine()->current, VM::Value::fromString(value->vmValue().asString()));
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<String> StringObject::StringValue() const
+{
+ VM::StringObject *s = ConstValuePtr(this)->asStringObject();
+ assert(s);
+ return Local<String>::New(Value::fromVmValue(s->value));
+}
+
+StringObject *StringObject::Cast(Value *obj)
+{
+ return static_cast<StringObject *>(obj);
+}
+
+Local<RegExp> RegExp::New(Handle<String> pattern, RegExp::Flags flags)
+{
+ int f = 0;
+ if (flags & kGlobal)
+ f |= V4IR::RegExp::RegExp_Global;
+ if (flags & kIgnoreCase)
+ f |= V4IR::RegExp::RegExp_IgnoreCase;
+ if (flags & kMultiline)
+ f |= V4IR::RegExp::RegExp_Multiline;
+ VM::Object *o = currentEngine()->newRegExpObject(pattern->asQString(), f);
+ return Local<RegExp>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<String> RegExp::GetSource() const
+{
+ RegExpObject *re = ConstValuePtr(this)->asRegExpObject();
+ assert(re);
+ return Local<String>::New(Value::fromVmValue(VM::Value::fromString(currentEngine()->current, re->value->pattern())));
+}
+
+RegExp::Flags RegExp::GetFlags() const
+{
+ RegExpObject *re = ConstValuePtr(this)->asRegExpObject();
+ assert(re);
+
+ int f = 0;
+ if (re->global)
+ f |= kGlobal;
+ if (re->value->ignoreCase())
+ f |= kIgnoreCase;
+ if (re->value->multiLine())
+ f |= kMultiline;
+
+ return (RegExp::Flags)f;
+}
+
+RegExp *RegExp::Cast(Value *obj)
+{
+ return static_cast<RegExp *>(obj);
+}
+
+struct VoidStarWrapper : public VM::Object::ExternalResource
+{
+ void *data;
+};
+
+Local<Value> External::Wrap(void *data)
+{
+ return New(data);
+}
+
+void *External::Unwrap(Handle<v8::Value> obj)
+{
+ return obj.As<External>()->Value();
+}
+
+Local<External> External::New(void *value)
+{
+ VM::Object *o = currentEngine()->newObject();
+ VoidStarWrapper *wrapper = new VoidStarWrapper;
+ wrapper->data = value;
+ o->externalResource = wrapper;
+ return Local<v8::External>::New(v8::Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+External *External::Cast(v8::Value *obj)
+{
+ return static_cast<External *>(obj);
+}
+
+void *External::Value() const
+{
+ VM::Object *o = ConstValuePtr(this)->asObject();
+ if (!o || !o->externalResource)
+ return 0;
+ return static_cast<VoidStarWrapper*>(o->externalResource)->data;
+}
+
+
+void Template::Set(Handle<String> name, Handle<Value> value, PropertyAttribute attributes)
+{
+ Property p;
+ p.name = Persistent<String>::New(name);
+ p.value = Persistent<Value>::New(value);
+ p.attributes = attributes;
+ m_properties << p;
+}
+
+void Template::Set(const char *name, Handle<Value> value)
+{
+ Set(String::New(name), value);
+}
+
+
+Arguments::Arguments(const VM::Value *args, int argc, const VM::Value &thisObject, bool isConstructor, const Persistent<Value> &data)
+{
+ for (int i = 0; i < argc; ++i)
+ m_args << Persistent<Value>::New(Value::fromVmValue(args[i]));
+ m_thisObject = Persistent<Object>::New(Value::fromVmValue(thisObject));
+ m_isConstructor = isConstructor;
+ m_data = Persistent<Value>::New(data);
+}
+
+int Arguments::Length() const
+{
+ return m_args.size();
+}
+
+Local<Value> Arguments::operator [](int i) const
+{
+ return Local<Value>::New(m_args.at(i));
+}
+
+Local<Object> Arguments::This() const
+{
+ return Local<Object>::New(m_thisObject);
+}
+
+Local<Object> Arguments::Holder() const
+{
+ // ### FIXME.
+ return Local<Object>::New(m_thisObject);
+}
+
+bool Arguments::IsConstructCall() const
+{
+ return m_isConstructor;
+}
+
+Local<Value> Arguments::Data() const
+{
+ return Local<Value>::New(m_data);
+}
+
+Isolate *Arguments::GetIsolate() const
+{
+ return Isolate::GetCurrent();
+}
+
+
+AccessorInfo::AccessorInfo(const VM::Value &thisObject, const Persistent<Value> &data)
+{
+ m_this = Persistent<Object>::New(Value::fromVmValue(thisObject));
+ m_data = data;
+}
+
+Isolate *AccessorInfo::GetIsolate() const
+{
+ return Isolate::GetCurrent();
+}
+
+Local<Value> AccessorInfo::Data() const
+{
+ return Local<Value>::New(m_data);
+}
+
+Local<Object> AccessorInfo::This() const
+{
+ return Local<Object>::New(m_this);
+}
+
+Local<Object> AccessorInfo::Holder() const
+{
+ // ### FIXME
+ return Local<Object>::New(m_this);
+}
+
+template <typename BaseClass>
+class V4V8Object : public BaseClass
+{
+public:
+ V4V8Object(VM::ExecutionEngine *engine, ObjectTemplate *tmpl)
+ : BaseClass(engine->rootContext)
+ {
+ this->vtbl = &static_vtbl;
+ m_template = Persistent<ObjectTemplate>(tmpl);
+ if (m_template.IsEmpty())
+ m_template = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+
+ foreach (const ObjectTemplate::Accessor &acc, m_template->m_accessors) {
+ PropertyAttributes attrs = Attr_Accessor;
+ attrs.setConfigurable(!(acc.attribute & DontDelete));
+ attrs.setEnumerable(!(acc.attribute & DontEnum));
+ VM::Property *pd = this->insertMember(acc.name->asVMString(), attrs);
+ *pd = VM::Property::fromAccessor(acc.getter->vmValue().asFunctionObject(),
+ acc.setter->vmValue().asFunctionObject());
+ }
+
+ initProperties(m_template.get());
+ }
+
+ void initProperties(Template *tmpl)
+ {
+ foreach (const Template::Property &p, tmpl->m_properties) {
+ PropertyAttributes attrs = Attr_Data;
+ attrs.setConfigurable(!(p.attributes & DontDelete));
+ attrs.setEnumerable(!(p.attributes & DontEnum));
+ attrs.setWritable(!(p.attributes & ReadOnly));
+ VM::Property *pd = this->insertMember(p.name->asVMString(), attrs);
+ *pd = VM::Property::fromValue(p.value->vmValue());
+ }
+ }
+
+ Persistent<ObjectTemplate> m_template;
+
+protected:
+ AccessorInfo namedAccessorInfo()
+ {
+ // ### thisObject?
+ return AccessorInfo(VM::Value::fromObject(this), m_template->m_namedPropertyData);
+ }
+ AccessorInfo fallbackAccessorInfo()
+ {
+ // ### thisObject?
+ return AccessorInfo(VM::Value::fromObject(this), m_template->m_fallbackPropertyData);
+ }
+ AccessorInfo indexedAccessorInfo()
+ {
+ // ### thisObject?
+ return AccessorInfo(VM::Value::fromObject(this), m_template->m_namedPropertyData);
+ }
+
+ static const ManagedVTable static_vtbl;
+
+ static VM::Value get(VM::Managed *m, ExecutionContext *ctx, VM::String *name, bool *hasProperty)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_namedPropertyGetter) {
+ Handle<Value> result = that->m_template->m_namedPropertyGetter(String::New(name), that->namedAccessorInfo());
+ if (!result.IsEmpty()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return result->vmValue();
+ }
+ }
+
+ bool hasProp = false;
+ VM::Value result = BaseClass::get(m, ctx, name, &hasProp);
+
+ if (!hasProp && that->m_template->m_fallbackPropertyGetter) {
+ Handle<Value> fallbackResult = that->m_template->m_fallbackPropertyGetter(String::New(name), that->fallbackAccessorInfo());
+ if (!fallbackResult.IsEmpty()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return fallbackResult->vmValue();
+ }
+ }
+
+ if (hasProperty)
+ *hasProperty = hasProp;
+ return result;
+ }
+
+ static VM::Value getIndexed(VM::Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_indexedPropertyGetter) {
+ Handle<Value> result = that->m_template->m_indexedPropertyGetter(index, that->indexedAccessorInfo());
+ if (!result.IsEmpty()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return result->vmValue();
+ }
+ }
+ return BaseClass::getIndexed(m, ctx, index, hasProperty);
+ }
+
+ static void put(VM::Managed *m, ExecutionContext *ctx, VM::String *name, const VM::Value &value)
+ {
+ Local<Value> v8Value = Local<Value>::New(Value::fromVmValue(value));
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_namedPropertySetter) {
+ Handle<Value> result = that->m_template->m_namedPropertySetter(String::New(name), v8Value, that->namedAccessorInfo());
+ if (!result.IsEmpty())
+ return;
+ }
+ PropertyAttributes attrs;
+ Property *pd = that->__getOwnProperty__(name, &attrs);
+ if (pd)
+ that->putValue(ctx, pd, attrs, value);
+ else if (that->m_template->m_fallbackPropertySetter)
+ that->m_template->m_fallbackPropertySetter(String::New(name), v8Value, that->fallbackAccessorInfo());
+ else
+ BaseClass::put(m, ctx, name, value);
+ }
+
+ static void putIndexed(VM::Managed *m, ExecutionContext *ctx, uint index, const VM::Value &value)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_indexedPropertySetter) {
+ Handle<Value> result = that->m_template->m_indexedPropertySetter(index, Local<Value>::New(Value::fromVmValue(value)), that->indexedAccessorInfo());
+ if (!result.IsEmpty())
+ return;
+ }
+ BaseClass::putIndexed(m, ctx, index, value);
+ }
+
+ static PropertyAttributes propertyAttributesToFlags(const Handle<Value> &attr)
+ {
+ PropertyAttributes flags;
+ int intAttr = attr->ToInt32()->Value();
+ flags.setWritable(!(intAttr & ReadOnly));
+ flags.setEnumerable(!(intAttr & DontEnum));
+ flags.setConfigurable(!(intAttr & DontDelete));
+ return flags;
+ }
+
+ static PropertyAttributes query(VM::Managed *m, ExecutionContext *ctx, VM::String *name)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_namedPropertyQuery) {
+ Handle<Value> result = that->m_template->m_namedPropertyQuery(String::New(name), that->namedAccessorInfo());
+ if (!result.IsEmpty())
+ return propertyAttributesToFlags(result);
+ }
+ PropertyAttributes flags = BaseClass::query(m, ctx, name);
+ if (flags.type() == PropertyAttributes::Generic && that->m_template->m_fallbackPropertySetter) {
+ Handle<Value> result = that->m_template->m_fallbackPropertyQuery(String::New(name), that->fallbackAccessorInfo());
+ if (!result.IsEmpty())
+ return propertyAttributesToFlags(result);
+ }
+
+ return flags;
+ }
+
+ static PropertyAttributes queryIndexed(VM::Managed *m, ExecutionContext *ctx, uint index)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_indexedPropertyQuery) {
+ Handle<Value> result = that->m_template->m_indexedPropertyQuery(index, that->indexedAccessorInfo());
+ if (!result.IsEmpty())
+ return propertyAttributesToFlags(result);
+ }
+
+ return BaseClass::queryIndexed(m, ctx, index);
+ }
+
+ static bool deleteProperty(VM::Managed *m, ExecutionContext *ctx, VM::String *name)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_namedPropertyDeleter) {
+ Handle<Boolean> result = that->m_template->m_namedPropertyDeleter(String::New(name), that->namedAccessorInfo());
+ if (!result.IsEmpty())
+ return result->Value();
+ }
+
+ bool result = BaseClass::deleteProperty(m, ctx, name);
+
+ if (that->m_template->m_fallbackPropertyDeleter) {
+ Handle<Boolean> interceptResult = that->m_template->m_fallbackPropertyDeleter(String::New(name), that->fallbackAccessorInfo());
+ if (!interceptResult.IsEmpty())
+ result = interceptResult->Value();
+ }
+
+ return result;
+ }
+
+ static bool deleteIndexedProperty(VM::Managed *m, ExecutionContext *ctx, uint index)
+ {
+ V4V8Object *that = static_cast<V4V8Object*>(m);
+ if (that->m_template->m_indexedPropertyDeleter) {
+ Handle<Boolean> result = that->m_template->m_indexedPropertyDeleter(index, that->indexedAccessorInfo());
+ if (!result.IsEmpty())
+ return result->Value();
+ }
+ return BaseClass::deleteIndexedProperty(m, ctx, index);
+ }
+};
+
+template<>
+DEFINE_MANAGED_VTABLE(V4V8Object<VM::Object>);
+template<>
+DEFINE_MANAGED_VTABLE(V4V8Object<VM::FunctionObject>);
+template<>
+DEFINE_MANAGED_VTABLE(V4V8Object<VM::FunctionPrototype>);
+
+struct V4V8Function : public V4V8Object<VM::FunctionObject>
+{
+ V4V8Function(VM::ExecutionEngine *engine, FunctionTemplate *functionTemplate)
+ : V4V8Object<VM::FunctionObject>(engine, 0)
+ {
+ vtbl = &static_vtbl;
+ m_functionTemplate = Persistent<FunctionTemplate>(functionTemplate);
+ initProperties(m_functionTemplate.get());
+ }
+
+protected:
+ static const ManagedVTable static_vtbl;
+
+ static VM::Value call(VM::Managed *m, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc)
+ {
+ V4V8Function *that = static_cast<V4V8Function*>(m);
+ Arguments arguments(args, argc, thisObject, false, that->m_functionTemplate->m_data);
+ VM::Value result = VM::Value::undefinedValue();
+ if (that->m_functionTemplate->m_callback)
+ result = that->m_functionTemplate->m_callback(arguments)->vmValue();
+ return result;
+ }
+
+ static VM::Value construct(VM::Managed *m, ExecutionContext *context, VM::Value *args, int argc)
+ {
+ V4V8Function *that = static_cast<V4V8Function*>(m);
+ Arguments arguments(args, argc, VM::Value::undefinedValue(), true, that->m_functionTemplate->m_data);
+
+ VM::Object *obj = that->m_functionTemplate->m_instanceTemplate->NewInstance()->vmValue().asObject();
+ VM::Value proto = that->Managed::get(context, context->engine->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+
+ VM::Value result = VM::Value::undefinedValue();
+ if (that->m_functionTemplate->m_callback)
+ result = that->m_functionTemplate->m_callback(arguments)->vmValue();
+ if (result.isObject())
+ return result;
+ return VM::Value::fromObject(obj);
+
+ }
+
+ Persistent<FunctionTemplate> m_functionTemplate;
+};
+
+DEFINE_MANAGED_VTABLE(V4V8Function);
+
+FunctionTemplate::FunctionTemplate(InvocationCallback callback, Handle<Value> data)
+ : m_callback(callback)
+{
+ m_instanceTemplate = Local<ObjectTemplate>();
+ m_prototypeTemplate = Local<ObjectTemplate>();
+ m_data = Persistent<Value>::New(data);
+}
+
+Local<FunctionTemplate> FunctionTemplate::New(InvocationCallback callback, Handle<Value> data)
+{
+ FunctionTemplate *ft = new FunctionTemplate(callback, data);
+ return Local<FunctionTemplate>::New(Handle<FunctionTemplate>(ft));
+}
+
+Local<Function> FunctionTemplate::GetFunction()
+{
+ VM::ExecutionEngine *engine = currentEngine();
+ VM::Object *o = new (engine->memoryManager) V4V8Function(engine, this);
+ VM::Object *proto = new (engine->memoryManager) V4V8Object<VM::FunctionPrototype>(engine, m_prototypeTemplate.get());
+ o->put(engine->current, engine->id_prototype, VM::Value::fromObject(proto));
+ return Local<Function>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<ObjectTemplate> FunctionTemplate::InstanceTemplate()
+{
+ if (m_instanceTemplate.IsEmpty())
+ m_instanceTemplate = ObjectTemplate::New();
+ return m_instanceTemplate;
+}
+
+Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate()
+{
+ if (m_prototypeTemplate.IsEmpty())
+ m_prototypeTemplate = ObjectTemplate::New();
+ return m_prototypeTemplate;
+}
+
+
+Local<ObjectTemplate> ObjectTemplate::New()
+{
+ ObjectTemplate *ot = new ObjectTemplate;
+ return Local<ObjectTemplate>::New(Handle<ObjectTemplate>(ot));
+}
+
+Local<Object> ObjectTemplate::NewInstance()
+{
+ VM::ExecutionEngine *engine = currentEngine();
+ VM::Object *o = new (engine->memoryManager) V4V8Object<VM::Object>(engine, this);
+ o->prototype = engine->objectPrototype;
+ o->externalComparison = m_useUserComparison;
+
+ return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+void ObjectTemplate::SetAccessor(Handle<String> name, AccessorGetter getter, AccessorSetter setter, Handle<Value> data, AccessControl settings, PropertyAttribute attribute)
+{
+ VM::ExecutionEngine *engine = currentEngine();
+
+ Accessor a;
+ if (getter) {
+ VM::FunctionObject *wrappedGetter = new (engine->memoryManager) V8AccessorGetter(engine->rootContext, name, getter, data);
+ a.getter = Persistent<Value>::New(Value::fromVmValue(VM::Value::fromObject(wrappedGetter)));
+ }
+ if (setter) {
+ VM::FunctionObject *wrappedSetter = new (engine->memoryManager) V8AccessorSetter(engine->rootContext, name, setter, data);
+ a.setter = Persistent<Value>::New(Value::fromVmValue(VM::Value::fromObject(wrappedSetter)));
+ }
+ a.attribute = attribute;
+ a.name = Persistent<String>::New(name);
+ m_accessors << a;
+}
+
+void ObjectTemplate::SetNamedPropertyHandler(NamedPropertyGetter getter, NamedPropertySetter setter, NamedPropertyQuery query, NamedPropertyDeleter deleter, NamedPropertyEnumerator enumerator, Handle<Value> data)
+{
+ m_namedPropertyGetter = getter;
+ m_namedPropertySetter = setter;
+ m_namedPropertyQuery = query;
+ m_namedPropertyDeleter = deleter;
+ m_namedPropertyEnumerator = enumerator;
+ m_namedPropertyData = Persistent<Value>::New(data);
+}
+
+void ObjectTemplate::SetFallbackPropertyHandler(NamedPropertyGetter getter, NamedPropertySetter setter, NamedPropertyQuery query, NamedPropertyDeleter deleter, NamedPropertyEnumerator enumerator, Handle<Value> data)
+{
+ m_fallbackPropertyGetter = getter;
+ m_fallbackPropertySetter = setter;
+ m_fallbackPropertyQuery = query;
+ m_fallbackPropertyDeleter = deleter;
+ m_fallbackPropertyEnumerator = enumerator;
+ m_fallbackPropertyData = Persistent<Value>::New(data);
+}
+
+void ObjectTemplate::SetIndexedPropertyHandler(IndexedPropertyGetter getter, IndexedPropertySetter setter, IndexedPropertyQuery query, IndexedPropertyDeleter deleter, IndexedPropertyEnumerator enumerator, Handle<Value> data)
+{
+ m_indexedPropertyGetter = getter;
+ m_indexedPropertySetter = setter;
+ m_indexedPropertyQuery = query;
+ m_indexedPropertyDeleter = deleter;
+ m_indexedPropertyEnumerator = enumerator;
+ m_indexedPropertyData = Persistent<Value>::New(data);
+}
+
+int ObjectTemplate::InternalFieldCount()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void ObjectTemplate::SetInternalFieldCount(int value)
+{
+ Q_UNIMPLEMENTED();
+}
+
+bool ObjectTemplate::HasExternalResource()
+{
+ // we always reserve the space for the external resource
+ return true;
+}
+
+void ObjectTemplate::SetHasExternalResource(bool value)
+{
+ // no need for this, we always reserve the space for the external resource
+ Q_UNUSED(value);
+}
+
+void ObjectTemplate::MarkAsUseUserObjectComparison()
+{
+ m_useUserComparison = true;
+}
+
+ObjectTemplate::ObjectTemplate()
+{
+ m_namedPropertyGetter = 0;
+ m_namedPropertySetter = 0;
+ m_namedPropertyQuery = 0;
+ m_namedPropertyDeleter = 0;
+ m_namedPropertyEnumerator = 0;
+
+ m_fallbackPropertyGetter = 0;
+ m_fallbackPropertySetter = 0;
+ m_fallbackPropertyQuery = 0;
+ m_fallbackPropertyDeleter = 0;
+ m_fallbackPropertyEnumerator = 0;
+
+ m_indexedPropertyGetter = 0;
+ m_indexedPropertySetter = 0;
+ m_indexedPropertyQuery = 0;
+ m_indexedPropertyDeleter = 0;
+ m_indexedPropertyEnumerator = 0;
+
+ m_useUserComparison = false;
+}
+
+Handle<Primitive> Undefined()
+{
+ Handle<Primitive> val;
+ val.val = VM::Value::undefinedValue().val;
+ return val;
+}
+
+Handle<Primitive> Null()
+{
+ Handle<Primitive> val;
+ val.val = VM::Value::nullValue().val;
+ return val;
+}
+
+Handle<Boolean> True()
+{
+ Handle<Primitive> val;
+ val.val = VM::Value::fromBoolean(true).val;
+ return val;
+}
+
+Handle<Boolean> False()
+{
+ Handle<Primitive> val;
+ val.val = VM::Value::fromBoolean(false).val;
+ return val;
+}
+
+
+Handle<Value> ThrowException(Handle<Value> exception)
+{
+ __qmljs_throw(currentEngine()->current, exception->vmValue());
+ return Handle<Value>();
+}
+
+
+Local<Value> Exception::ReferenceError(Handle<String> message)
+{
+ Q_UNUSED(message);
+ VM::Object *o = currentEngine()->newReferenceErrorObject(currentEngine()->current, message->ToString()->asQString());
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<Value> Exception::SyntaxError(Handle<String> message)
+{
+ Q_UNUSED(message);
+ VM::Object *o = currentEngine()->newSyntaxErrorObject(currentEngine()->current, 0);
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<Value> Exception::TypeError(Handle<String> message)
+{
+ Q_UNUSED(message);
+ VM::Object *o = currentEngine()->newTypeErrorObject(currentEngine()->current, message->ToString()->asQString());
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+Local<Value> Exception::Error(Handle<String> message)
+{
+ Q_UNUSED(message);
+ VM::Object *o = currentEngine()->newErrorObject(VM::Value::fromString(currentEngine()->current, message->ToString()->asQString()));
+ return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o)));
+}
+
+
+static QThreadStorage<Isolate*> currentIsolate;
+
+Isolate::Isolate()
+ : m_lastIsolate(0)
+ , tryCatch(0)
+{
+}
+
+Isolate::~Isolate()
+{
+}
+
+Isolate *Isolate::New()
+{
+ assert(!"Isolate::New()");
+ Q_UNREACHABLE();
+}
+
+void Isolate::Enter()
+{
+ m_lastIsolate = currentIsolate.localData();
+ currentIsolate.localData() = this;
+}
+
+void Isolate::Exit()
+{
+ currentIsolate.localData() = m_lastIsolate;
+ m_lastIsolate = 0;
+}
+
+void Isolate::Dispose()
+{
+ Q_UNIMPLEMENTED();
+}
+
+void Isolate::SetData(void *data)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void *Isolate::GetData()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void Isolate::setException(const VM::Value &ex)
+{
+ if (tryCatch) {
+ tryCatch->hasCaughtException = true;
+ tryCatch->exception = Local<Value>::New(Value::fromVmValue(ex));
+ }
+}
+
+Isolate *Isolate::GetCurrent()
+{
+ if (!currentIsolate.hasLocalData())
+ currentIsolate.setLocalData(new Isolate);
+ return currentIsolate.localData();
+}
+
+
+void V8::SetFlagsFromString(const char *, int)
+{
+ // we can safely ignore these
+}
+
+static UserObjectComparisonCallback userObjectComparisonCallback = 0;
+
+static bool v8ExternalResourceComparison(const VM::Value &a, const VM::Value &b)
+{
+ if (!userObjectComparisonCallback)
+ return false;
+ Local<Object> la = Local<Object>::New(Value::fromVmValue(a));
+ Local<Object> lb = Local<Object>::New(Value::fromVmValue(b));
+ return userObjectComparisonCallback(la, lb);
+}
+
+void V8::SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback callback)
+{
+ userObjectComparisonCallback = callback;
+ currentEngine()->externalResourceComparison = v8ExternalResourceComparison;
+}
+
+void V8::AddGCPrologueCallback(GCPrologueCallback, GCType)
+{
+ // not required currently as we don't have weak Persistent references.
+ // not having them will lead to some leaks in QQmlVMEMetaObejct, but shouldn't matter otherwise
+}
+
+void V8::RemoveGCPrologueCallback(GCPrologueCallback)
+{
+ assert(!"RemoveGCPrologueCallback();");
+}
+
+void V8::AddImplicitReferences(Persistent<Object> parent, Persistent<Value> *children, size_t length)
+{
+ // not required currently as we don't have weak Persistent references.
+ // not having them will lead to some leaks in QQmlVMEMetaObejct, but shouldn't matter otherwise
+ assert(!"AddImplicitReferences();");
+}
+
+bool V8::Initialize()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+bool V8::Dispose()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+bool V8::IdleNotification(int hint)
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void V8::LowMemoryNotification()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+
+TryCatch::TryCatch()
+{
+ Isolate *i = Isolate::GetCurrent();
+
+ hasCaughtException = false;
+ parent = i->tryCatch;
+ i->tryCatch = this;
+}
+
+TryCatch::~TryCatch()
+{
+ Isolate *i = Isolate::GetCurrent();
+ i->tryCatch = parent;
+}
+
+bool TryCatch::HasCaught() const
+{
+ return hasCaughtException;
+}
+
+Handle<Value> TryCatch::ReThrow()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+Local<Value> TryCatch::Exception() const
+{
+ return exception;
+}
+
+Local<Message> TryCatch::Message() const
+{
+ Q_UNIMPLEMENTED();
+ return Local<v8::Message>::New(Handle<v8::Message>(new v8::Message(QString(), QString(), 0)));
+}
+
+void TryCatch::Reset()
+{
+ hasCaughtException = false;
+}
+
+
+
+struct Context::Private
+{
+ Private()
+ {
+ engine.reset(new QQmlJS::VM::ExecutionEngine);
+ }
+
+ QScopedPointer<QQmlJS::VM::ExecutionEngine> engine;
+};
+
+Context::Context()
+ : m_lastContext(0)
+ , d(new Private)
+{
+}
+
+Context::~Context()
+{
+ delete d;
+}
+
+Persistent<Context> Context::New(ExtensionConfiguration *extensions, Handle<ObjectTemplate> global_template, Handle<Value> global_object)
+{
+ Context *result = new Context;
+ return Persistent<Context>::New(Handle<Context>(result));
+}
+
+Local<Object> Context::Global()
+{
+ return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(d->engine->globalObject)));
+}
+
+Local<Context> Context::GetCurrent()
+{
+ return Context::Adopt(Isolate::GetCurrent()->m_contextStack.top());
+}
+
+Local<Context> Context::GetCalling()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+Local<Object> Context::GetCallingQmlGlobal()
+{
+ VM::ExecutionEngine *engine = GetCurrent()->GetEngine();
+ VM::ExecutionContext *ctx = engine->current;
+ while (ctx && ctx->outer != engine->rootContext)
+ ctx = ctx->outer;
+
+ assert(ctx);
+ if (!ctx->type == ExecutionContext::Type_QmlContext)
+ return Local<Object>();
+
+ return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(static_cast<CallContext *>(ctx)->activation)));
+}
+
+Local<Value> Context::GetCallingScriptData()
+{
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+}
+
+void Context::Enter()
+{
+ Isolate* iso = Isolate::GetCurrent();
+ iso->m_contextStack.push(this);
+}
+
+void Context::Exit()
+{
+ Isolate::GetCurrent()->m_contextStack.pop();
+}
+
+
+QQmlJS::VM::ExecutionEngine *Context::GetEngine()
+{
+ return d->engine.data();
+}
+
+
+}
diff --git a/src/qml/qml/v4vm/qv4v8.h b/src/qml/qml/v4vm/qv4v8.h
new file mode 100644
index 0000000000..bcd6a7fef3
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4v8.h
@@ -0,0 +1,2581 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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$
+**
+****************************************************************************/
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/** \mainpage V8 API Reference Guide
+ *
+ * V8 is Google's open source JavaScript engine.
+ *
+ * This set of documents provides reference material generated from the
+ * V8 header file, include/v8.h.
+ *
+ * For other documentation see http://code.google.com/apis/v8/
+ */
+
+#ifndef V8_H_
+#define V8_H_
+
+#include "qv4global.h"
+#include "qv4string.h"
+#include <QStack>
+#include <QSharedData>
+
+namespace QQmlJS {
+namespace VM {
+struct Value;
+struct String;
+struct ExecutionEngine;
+struct Object;
+class MemoryManager;
+}
+}
+
+#include <stdint.h>
+
+#define V8EXPORT Q_V4_EXPORT
+
+/**
+ * The v8 JavaScript engine.
+ */
+namespace v8 {
+
+class Context;
+class String;
+class StringObject;
+class Value;
+class Utils;
+class Number;
+class NumberObject;
+class Object;
+class Array;
+class Int32;
+class Uint32;
+class External;
+class Primitive;
+class Boolean;
+class BooleanObject;
+class Integer;
+class Function;
+class Date;
+class ImplementationUtilities;
+class Signature;
+class AccessorSignature;
+template <class T> struct Handle;
+template <class T> class Local;
+template <class T> class Persistent;
+class FunctionTemplate;
+class ObjectTemplate;
+class Data;
+class AccessorInfo;
+class StackTrace;
+class StackFrame;
+class Isolate;
+class TryCatch;
+
+V8EXPORT void *gcProtect(void *handle);
+V8EXPORT void gcProtect(void *memoryManager, void *handle);
+V8EXPORT void gcUnprotect(void *memoryManager, void *handle);
+
+// --- Weak Handles ---
+
+/**
+ * A weak reference callback function.
+ *
+ * This callback should either explicitly invoke Dispose on |object| if
+ * V8 wrapper is not needed anymore, or 'revive' it by invocation of MakeWeak.
+ *
+ * \param object the weak global object to be reclaimed by the garbage collector
+ * \param parameter the value passed in when making the weak global object
+ */
+typedef void (*WeakReferenceCallback)(Persistent<Value> object,
+ void* parameter);
+
+
+// --- Handles ---
+
+#define TYPE_CHECK(T, S) \
+ while (false) { \
+ *(static_cast<T* volatile*>(0)) = static_cast<S*>(0); \
+ }
+
+/**
+ * An object reference managed by the v8 garbage collector.
+ *
+ * All objects returned from v8 have to be tracked by the garbage
+ * collector so that it knows that the objects are still alive. Also,
+ * because the garbage collector may move objects, it is unsafe to
+ * point directly to an object. Instead, all objects are stored in
+ * handles which are known by the garbage collector and updated
+ * whenever an object moves. Handles should always be passed by value
+ * (except in cases like out-parameters) and they should never be
+ * allocated on the heap.
+ *
+ * There are two types of handles: local and persistent handles.
+ * Local handles are light-weight and transient and typically used in
+ * local operations. They are managed by HandleScopes. Persistent
+ * handles can be used when storing objects across several independent
+ * operations and have to be explicitly deallocated when they're no
+ * longer used.
+ *
+ * It is safe to extract the object stored in the handle by
+ * dereferencing the handle (for instance, to extract the Object* from
+ * a Handle<Object>); the value will still be governed by a handle
+ * behind the scenes and the same rules apply to these values as to
+ * their handles.
+ */
+
+template <typename T>
+struct Handle;
+
+template <typename T>
+struct HandleOperations
+{
+ static void init(Handle<T> *handle)
+ {
+#if QT_POINTER_SIZE == 8
+ handle->val = quint64(Handle<T>::_Null_Type) << Handle<T>::Tag_Shift;
+#else
+ handle->tag = Handle<T>::_Null_Type;
+ handle->int_32 = 0;
+#endif
+ }
+
+ static void ref(Handle<T> *)
+ {
+ }
+
+ static void deref(Handle<T> *)
+ {
+ }
+
+ static void *protect(Handle<T> *handle)
+ {
+ return gcProtect(handle);
+ }
+
+ static void protect(void *memoryManager, Handle<T> *handle)
+ {
+ gcProtect(memoryManager, handle);
+ }
+
+ static void unProtect(void *memoryManager, Handle<T> *handle)
+ {
+ gcUnprotect(memoryManager, handle);
+ }
+
+ static bool isEmpty(const Handle<T> *handle)
+ {
+ return handle->tag == Handle<T>::_Null_Type;
+ }
+
+ static T *get(const Handle<T> *handle)
+ {
+ return const_cast<T*>(reinterpret_cast<const T*>(handle));
+ }
+};
+
+#define DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Type) \
+ template <> \
+ struct HandleOperations<Type> \
+ { \
+ static void init(Handle<Type> *handle) \
+ { \
+ handle->object = 0; \
+ } \
+ \
+ static void ref(Handle<Type> *handle) \
+ { \
+ if (handle->object) \
+ handle->object->ref.ref(); \
+ } \
+ \
+ static void deref(Handle<Type> *handle) \
+ { \
+ if (handle->object && !handle->object->ref.deref()) { \
+ delete handle->object; \
+ handle->object = 0; \
+ } \
+ } \
+ static void *protect(Handle<Type> *) { return 0; } \
+ static void protect(void *, Handle<Type> *) {} \
+ static void unProtect(void *, Handle<Type> *) {} \
+ static bool isEmpty(const Handle<Type> *handle) \
+ { \
+ return handle->object == 0; \
+ } \
+ static Type *get(const Handle<Type> *handle) \
+ { \
+ return handle->object; \
+ } \
+ \
+ };
+
+template <typename T>
+struct Handle {
+ Handle()
+ {
+ HandleOperations<T>::init(this);
+ }
+ template <typename Other>
+ Handle(const Handle<Other> &that)
+ : val(that.val)
+ {
+ HandleOperations<T>::ref(this);
+ }
+
+ explicit Handle(T *obj)
+ {
+ object = obj;
+ HandleOperations<T>::ref(this);
+ }
+
+ Handle(const Handle<T> &other)
+ : val(other.val)
+ {
+ HandleOperations<T>::ref(this);
+ }
+ Handle<T> &operator=(const Handle<T> &other)
+ {
+ if (this == &other)
+ return *this;
+ HandleOperations<T>::deref(this);
+ this->val = other.val;
+ HandleOperations<T>::ref(this);
+ return *this;
+ }
+ ~Handle()
+ {
+ HandleOperations<T>::deref(this);
+ }
+
+ bool IsEmpty() const { return HandleOperations<T>::isEmpty(this); }
+
+ T *operator->() const { return HandleOperations<T>::get(this); }
+
+ T *get() const { return HandleOperations<T>::get(this); }
+
+ template <typename Source>
+ static Handle<T> Cast(Handle<Source> that)
+ {
+ return that.template As<T>();
+ }
+
+ template <typename Target>
+ Handle<Target> As()
+ {
+ return Handle<Target>(*this);
+ }
+
+ void Clear()
+ {
+ val = 0;
+ }
+
+ template <class S> inline bool operator==(Handle<S> that) const {
+ return val == that.val;
+ }
+ template <class S> inline bool operator!=(Handle<S> that) const {
+ return val != that.val;
+ }
+
+ enum Masks {
+ NotDouble_Mask = 0xfffc0000,
+ Type_Mask = 0xffff8000,
+ Immediate_Mask = NotDouble_Mask | 0x00008000,
+ 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
+ };
+
+ enum ImmediateFlags {
+ ConvertibleToInt = Immediate_Mask | 0x1
+ };
+
+ enum ValueTypeInternal {
+ _Undefined_Type = Undefined_Type,
+ _Null_Type = Null_Type | ConvertibleToInt,
+ _Boolean_Type = Boolean_Type | ConvertibleToInt,
+ _Integer_Type = Integer_Type | ConvertibleToInt,
+ _Object_Type = Object_Type,
+ _String_Type = String_Type
+
+ };
+
+ union {
+ T *object;
+ 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
+ T *o;
+#endif
+ };
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint tag;
+#endif
+ };
+ };
+};
+
+
+/**
+ * A light-weight stack-allocated object handle. All operations
+ * that return objects from within v8 return them in local handles. They
+ * are created within HandleScopes, and all local handles allocated within a
+ * handle scope are destroyed when the handle scope is destroyed. Hence it
+ * is not necessary to explicitly deallocate local handles.
+ */
+template <class T> class Local : public Handle<T> {
+ public:
+ Local() {}
+ template <class S> Local(Local<S> that)
+ : Handle<T>(Handle<T>::Cast(that)) {
+ /**
+ * This check fails when trying to convert between incompatible
+ * handles. For example, converting from a Handle<String> to a
+ * Handle<Number>.
+ */
+ TYPE_CHECK(T, S);
+ }
+ template <class S> Local(S* that) : Handle<T>(that) { }
+ template <class S> static Local<T> Cast(Local<S> that) {
+#ifdef V8_ENABLE_CHECKS
+ // If we're going to perform the type check then we have to check
+ // that the handle isn't empty before doing the checked cast.
+ if (that.IsEmpty()) return Local<T>();
+#endif
+ return Local<T>::New(Handle<T>::Cast(that));
+ }
+
+ template <class S> Local<S> As() {
+ return Local<S>::Cast(*this);
+ }
+
+ /** Create a local handle for the content of another handle.
+ * The referee is kept alive by the local handle even when
+ * the original handle is destroyed/disposed.
+ */
+ static Local<T> New(Handle<T> that)
+ {
+ Local<T> result;
+ result.Handle<T>::operator =(that);
+ return result;
+ }
+};
+
+
+/**
+ * An object reference that is independent of any handle scope. Where
+ * a Local handle only lives as long as the HandleScope in which it was
+ * allocated, a Persistent handle remains valid until it is explicitly
+ * disposed.
+ *
+ * A persistent handle contains a reference to a storage cell within
+ * the v8 engine which holds an object value and which is updated by
+ * the garbage collector whenever the object is moved. A new storage
+ * cell can be created using Persistent::New and existing handles can
+ * be disposed using Persistent::Dispose. Since persistent handles
+ * are passed by value you may have many persistent handle objects
+ * that point to the same storage cell. For instance, if you pass a
+ * persistent handle as an argument to a function you will not get two
+ * different storage cells but rather two references to the same
+ * storage cell.
+ */
+template <class T> class Persistent : public Handle<T> {
+ public:
+ /**
+ * Creates an empty persistent handle that doesn't point to any
+ * storage cell.
+ */
+ Persistent() {}
+ ~Persistent() {
+ HandleOperations<T>::unProtect(m_memoryManager, this);
+ }
+
+ Persistent(const Persistent &other)
+ : Handle<T>(other)
+ , m_memoryManager(other.m_memoryManager)
+ {
+ HandleOperations<T>::protect(m_memoryManager, this);
+ }
+
+ Persistent &operator =(const Persistent &other)
+ {
+ if (&other == this)
+ return *this;
+ HandleOperations<T>::unProtect(m_memoryManager, this);
+ Handle<T>::operator =(other);
+ m_memoryManager = other.m_memoryManager;
+ HandleOperations<T>::protect(m_memoryManager, this);
+ return *this;
+ }
+
+ /**
+ * Creates a persistent handle for the same storage cell as the
+ * specified handle. This constructor allows you to pass persistent
+ * handles as arguments by value and to assign between persistent
+ * handles. However, attempting to assign between incompatible
+ * persistent handles, for instance from a Persistent<String> to a
+ * Persistent<Number> will cause a compile-time error. Assigning
+ * between compatible persistent handles, for instance assigning a
+ * Persistent<String> to a variable declared as Persistent<Value>,
+ * is allowed as String is a subclass of Value.
+ */
+ template <class S> Persistent(Persistent<S> that)
+ : Handle<T>(Handle<T>::Cast(that)) {
+ m_memoryManager = that.m_memoryManager;
+ HandleOperations<T>::protect(m_memoryManager, this);
+ }
+
+ template <class S> Persistent(S* that) : Handle<T>(that)
+ {
+ m_memoryManager = HandleOperations<T>::protect(this);
+ }
+
+ /**
+ * "Casts" a plain handle which is known to be a persistent handle
+ * to a persistent handle.
+ */
+ template <class S> explicit Persistent(Handle<S> that)
+ : Handle<T>(*that)
+ {
+ m_memoryManager = HandleOperations<T>::protect(this);
+ }
+
+ template <class S> static Persistent<T> Cast(Persistent<S> that) {
+ return Persistent<T>(T::Cast(*that));
+ }
+
+ template <class S> Persistent<S> As() {
+ return Persistent<S>::Cast(*this);
+ }
+
+ /**
+ * Creates a new persistent handle for an existing local or
+ * persistent handle.
+ */
+ static Persistent<T> New(Handle<T> that)
+ {
+ Persistent<T> result;
+ result.Handle<T>::operator =(that);
+ result.m_memoryManager = HandleOperations<T>::protect(&result);
+ return result;
+ }
+
+ /**
+ * Releases the storage cell referenced by this persistent handle.
+ * Does not remove the reference to the cell from any handles.
+ * This handle's reference, and any other references to the storage
+ * cell remain and IsEmpty will still return false.
+ */
+ void Dispose() {
+ HandleOperations<T>::unProtect(m_memoryManager, this);
+ m_memoryManager = 0;
+ HandleOperations<T>::deref(this);
+ HandleOperations<T>::init(this);
+ }
+
+ void Dispose(Isolate*) {
+ Dispose();
+ }
+
+ /**
+ * Make the reference to this object weak. When only weak handles
+ * refer to the object, the garbage collector will perform a
+ * callback to the given V8::WeakReferenceCallback function, passing
+ * it the object reference and the given parameters.
+ */
+ void MakeWeak(void* parameters, WeakReferenceCallback callback);
+public:
+ void *m_memoryManager;
+};
+
+
+ /**
+ * A stack-allocated class that governs a number of local handles.
+ * After a handle scope has been created, all local handles will be
+ * allocated within that handle scope until either the handle scope is
+ * deleted or another handle scope is created. If there is already a
+ * handle scope and a new one is created, all allocations will take
+ * place in the new handle scope until it is deleted. After that,
+ * new handles will again be allocated in the original handle scope.
+ *
+ * After the handle scope of a local handle has been deleted the
+ * garbage collector will no longer track the object stored in the
+ * handle and may deallocate it. The behavior of accessing a handle
+ * for which the handle scope has been deleted is undefined.
+ */
+class V8EXPORT HandleScope {
+ public:
+ HandleScope() {}
+
+ ~HandleScope() {}
+
+ /**
+ * Closes the handle scope and returns the value as a handle in the
+ * previous scope, which is the new current scope after the call.
+ */
+ template <class T> Local<T> Close(Handle<T> value) { return Local<T>::New(value); }
+};
+
+
+// --- Special objects ---
+
+
+/**
+ * The superclass of values and API object templates.
+ */
+class V8EXPORT Data : public QSharedData {
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Data)
+
+/**
+ * The origin, within a file, of a script.
+ */
+class V8EXPORT ScriptOrigin {
+public:
+ ScriptOrigin() : m_lineNumber(0), m_columnNumber(0) {}
+
+ ScriptOrigin(
+ Handle<Value> resource_name,
+ Handle<Integer> resource_line_offset = Handle<Integer>(),
+ Handle<Integer> resource_column_offset = Handle<Integer>());
+ Handle<Value> ResourceName() const;
+ Handle<Integer> ResourceLineOffset() const;
+ Handle<Integer> ResourceColumnOffset() const;
+private:
+ QString m_fileName;
+ int m_lineNumber, m_columnNumber;
+ friend class Script;
+};
+
+class ScriptData;
+
+/**
+ * A compiled JavaScript script.
+ */
+class V8EXPORT Script : public QSharedData {
+ public:
+ enum CompileFlags {
+ Default = 0x00,
+ QmlMode = 0x01,
+ NativeMode = 0x02
+ };
+
+ /**
+ * Compiles the specified script (context-independent).
+ *
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when New() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when New() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but allows data to be
+ * available to compile event handlers.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ ScriptOrigin* origin = NULL,
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>(),
+ CompileFlags = Default);
+
+ /**
+ * Compiles the specified script using the specified file name
+ * object (typically a string) as the script's origin.
+ *
+ * \param source Script source code.
+ * \param file_name file name object (typically a string) to be used
+ * as the script's origin.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ Handle<Value> file_name,
+ CompileFlags = Default);
+
+ /**
+ * Compiles the specified script (bound to current context).
+ *
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when Compile() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when Compile() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
+ */
+ static Local<Script> Compile(Handle<String> source,
+ ScriptOrigin* origin = NULL,
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>(),
+ CompileFlags = Default);
+
+ /**
+ * Compiles the specified script using the specified file name
+ * object (typically a string) as the script's origin.
+ *
+ * \param source Script source code.
+ * \param file_name File name to use as script's origin
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
+ */
+ static Local<Script> Compile(Handle<String> source,
+ Handle<Value> file_name,
+ Handle<String> script_data = Handle<String>(),
+ CompileFlags = Default);
+
+ /**
+ * Runs the script returning the resulting value. If the script is
+ * context independent (created using ::New) it will be run in the
+ * currently entered context. If it is context specific (created
+ * using ::Compile) it will be run in the context in which it was
+ * compiled.
+ */
+ Local<Value> Run();
+ Local<Value> Run(Handle<Object> qml);
+
+ /**
+ * Returns the script id value.
+ */
+ Local<Value> Id();
+
+ /**
+ * Associate an additional data object with the script. This is mainly used
+ * with the debugger as this data object is only available through the
+ * debugger API.
+ */
+ void SetData(Handle<String> data);
+
+private:
+ QString m_script;
+ ScriptOrigin m_origin;
+ CompileFlags m_flags;
+ Handle<Context> m_context;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Script)
+
+/**
+ * An error message.
+ */
+class V8EXPORT Message : public QSharedData {
+ public:
+ Message(const QString &message, const QString &resourceName, int lineNumber)
+ : m_message(message), m_resourceName(resourceName), m_lineNumber(lineNumber) {}
+
+ Local<String> Get() const;
+ /**
+ * Returns the resource name for the script from where the function causing
+ * the error originates.
+ */
+ Handle<Value> GetScriptResourceName() const;
+
+ /**
+ * Returns the number, 1-based, of the line where the error occurred.
+ */
+ int GetLineNumber() const;
+
+private:
+ QString m_message;
+ QString m_resourceName;
+ int m_lineNumber;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Message)
+
+/**
+ * Representation of a JavaScript stack trace. The information collected is a
+ * snapshot of the execution stack and the information remains valid after
+ * execution continues.
+ */
+class V8EXPORT StackTrace : public QSharedData
+{
+ public:
+ /**
+ * Flags that determine what information is placed captured for each
+ * StackFrame when grabbing the current stack trace.
+ */
+ enum StackTraceOptions {
+ kLineNumber = 1,
+ kColumnOffset = 1 << 1 | kLineNumber,
+ kScriptName = 1 << 2,
+ kFunctionName = 1 << 3,
+ kIsEval = 1 << 4,
+ kIsConstructor = 1 << 5,
+ kScriptNameOrSourceURL = 1 << 6,
+ kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName,
+ kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL
+ };
+
+ /**
+ * Returns a StackFrame at a particular index.
+ */
+ Local<StackFrame> GetFrame(uint32_t index) const;
+
+ /**
+ * Returns the number of StackFrames.
+ */
+ int GetFrameCount() const;
+
+ /**
+ * Returns StackTrace as a v8::Array that contains StackFrame objects.
+ */
+ Local<Array> AsArray();
+
+ /**
+ * Grab a snapshot of the current JavaScript execution stack.
+ *
+ * \param frame_limit The maximum number of stack frames we want to capture.
+ * \param options Enumerates the set of things we will capture for each
+ * StackFrame.
+ */
+ static Local<StackTrace> CurrentStackTrace(
+ int frame_limit,
+ StackTraceOptions options = kOverview);
+
+ private:
+ QVector<Local<StackFrame> > frames;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackTrace)
+
+
+/**
+ * A single JavaScript stack frame.
+ */
+class V8EXPORT StackFrame : public QSharedData {
+ public:
+ /**
+ * Returns the number, 1-based, of the line for the associate function call.
+ * This method will return Message::kNoLineNumberInfo if it is unable to
+ * retrieve the line number, or if kLineNumber was not passed as an option
+ * when capturing the StackTrace.
+ */
+ int GetLineNumber() const;
+
+ /**
+ * Returns the 1-based column offset on the line for the associated function
+ * call.
+ * This method will return Message::kNoColumnInfo if it is unable to retrieve
+ * the column number, or if kColumnOffset was not passed as an option when
+ * capturing the StackTrace.
+ */
+ int GetColumn() const;
+
+ /**
+ * Returns the name of the resource that contains the script for the
+ * function for this StackFrame.
+ */
+ Local<String> GetScriptName() const;
+
+ /**
+ * Returns the name of the resource that contains the script for the
+ * function for this StackFrame or sourceURL value if the script name
+ * is undefined and its source ends with //@ sourceURL=... string.
+ */
+ Local<String> GetScriptNameOrSourceURL() const;
+
+ /**
+ * Returns the name of the function associated with this stack frame.
+ */
+ Local<String> GetFunctionName() const;
+
+private:
+ friend class StackTrace;
+ StackFrame(Handle<String> script, Handle<String> function, int line, int column);
+ int m_lineNumber;
+ int m_columnNumber;
+ Persistent<String> m_scriptName;
+ Persistent<String> m_functionName;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackFrame)
+
+// --- Value ---
+
+
+/**
+ * The superclass of all JavaScript values and objects.
+ */
+class V8EXPORT Value {
+ public:
+ /**
+ * Returns true if this value is the undefined value. See ECMA-262
+ * 4.3.10.
+ */
+ bool IsUndefined() const;
+
+ /**
+ * Returns true if this value is the null value. See ECMA-262
+ * 4.3.11.
+ */
+ bool IsNull() const;
+
+ /**
+ * Returns true if this value is true.
+ */
+ bool IsTrue() const;
+
+ /**
+ * Returns true if this value is false.
+ */
+ bool IsFalse() const;
+
+ /**
+ * Returns true if this value is an instance of the String type.
+ * See ECMA-262 8.4.
+ */
+ bool IsString() const;
+
+ /**
+ * Returns true if this value is a function.
+ */
+ bool IsFunction() const;
+
+ /**
+ * Returns true if this value is an array.
+ */
+ bool IsArray() const;
+
+ /**
+ * Returns true if this value is an object.
+ */
+ bool IsObject() const;
+
+ /**
+ * Returns true if this value is boolean.
+ */
+ bool IsBoolean() const;
+
+ /**
+ * Returns true if this value is a number.
+ */
+ bool IsNumber() const;
+
+ /**
+ * Returns true if this value is external.
+ */
+ bool IsExternal() const;
+
+ /**
+ * Returns true if this value is a 32-bit signed integer.
+ */
+ bool IsInt32() const;
+
+ /**
+ * Returns true if this value is a 32-bit unsigned integer.
+ */
+ bool IsUint32() const;
+
+ /**
+ * Returns true if this value is a Date.
+ */
+ bool IsDate() const;
+
+ /**
+ * Returns true if this value is a Boolean object.
+ */
+ bool IsBooleanObject() const;
+
+ /**
+ * Returns true if this value is a Number object.
+ */
+ bool IsNumberObject() const;
+
+ /**
+ * Returns true if this value is a String object.
+ */
+ bool IsStringObject() const;
+
+ /**
+ * Returns true if this value is a RegExp.
+ */
+ bool IsRegExp() const;
+
+ /**
+ * Returns true if this value is an Error.
+ */
+ bool IsError() const;
+
+ Local<Boolean> ToBoolean() const;
+ Local<Number> ToNumber() const;
+ Local<String> ToString() const;
+ Local<Object> ToObject() const;
+ Local<Integer> ToInteger() const;
+ Local<Uint32> ToUint32() const;
+ Local<Int32> ToInt32() const;
+
+ /**
+ * Attempts to convert a string to an array index.
+ * Returns an empty handle if the conversion fails.
+ */
+ Local<Uint32> ToArrayIndex() const;
+
+ bool BooleanValue() const;
+ double NumberValue() const;
+ int64_t IntegerValue() const;
+ uint32_t Uint32Value() const;
+ int32_t Int32Value() const;
+
+ /** JS == */
+ bool Equals(Handle<Value> that) const;
+ bool StrictEquals(Handle<Value> that) const;
+
+ static Handle<Value> NewFromInternalValue(quint64 val)
+ {
+ Handle<Value> res;
+ res.val = val;
+ return res;
+ }
+
+ QQmlJS::VM::Value vmValue() const;
+ static Handle<Value> fromVmValue(const QQmlJS::VM::Value &vmValue);
+
+};
+
+
+/**
+ * The superclass of primitive values. See ECMA-262 4.3.2.
+ */
+class V8EXPORT Primitive : public Value { };
+
+
+/**
+ * A primitive boolean value (ECMA-262, 4.3.14). Either the true
+ * or false value.
+ */
+class V8EXPORT Boolean : public Primitive {
+ public:
+ bool Value() const;
+ static Handle<Boolean> New(bool value);
+};
+
+
+/**
+ * A JavaScript string value (ECMA-262, 4.3.17).
+ */
+class V8EXPORT String : public Primitive {
+ public:
+ /**
+ * Returns the number of characters in this string.
+ */
+ int Length() const;
+
+
+ /**
+ * Returns the hash of this string.
+ */
+ uint32_t Hash() const;
+
+ struct CompleteHashData {
+ CompleteHashData() : length(0), hash(0), symbol_id(0) {}
+ int length;
+ uint32_t hash;
+ uint32_t symbol_id;
+ };
+
+ /**
+ * Returns the "complete" hash of the string. This is
+ * all the information about the string needed to implement
+ * a very efficient hash keyed on the string.
+ *
+ * The members of CompleteHashData are:
+ * length: The length of the string. Equivalent to Length()
+ * hash: The hash of the string. Equivalent to Hash()
+ * symbol_id: If the string is a sequential symbol, the symbol
+ * id, otherwise 0. If the symbol ids of two strings are
+ * the same (and non-zero) the two strings are identical.
+ * If the symbol ids are different the strings may still be
+ * identical, but an Equals() check must be performed.
+ */
+ CompleteHashData CompleteHash() const;
+
+ /**
+ * Compute a hash value for the passed UTF16 string
+ * data.
+ */
+ static uint32_t ComputeHash(uint16_t *string, int length);
+ static uint32_t ComputeHash(char *string, int length);
+
+ /**
+ * Returns true if this string is equal to the external
+ * string data provided.
+ */
+ bool Equals(uint16_t *string, int length);
+ bool Equals(char *string, int length);
+ bool Equals(Handle<Value> that) const {
+ return v8::Value::Equals(that);
+ }
+
+ /**
+ * Write the contents of the string to an external buffer.
+ * If no arguments are given, expects the buffer to be large
+ * enough to hold the entire string and NULL terminator. Copies
+ * the contents of the string and the NULL terminator into the
+ * buffer.
+ *
+ * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop
+ * before the end of the buffer.
+ *
+ * Copies up to length characters into the output buffer.
+ * Only null-terminates if there is enough space in the buffer.
+ *
+ * \param buffer The buffer into which the string will be copied.
+ * \param start The starting position within the string at which
+ * copying begins.
+ * \param length The number of characters to copy from the string. For
+ * WriteUtf8 the number of bytes in the buffer.
+ * \param nchars_ref The number of characters written, can be NULL.
+ * \param options Various options that might affect performance of this or
+ * subsequent operations.
+ * \return The number of characters copied to the buffer excluding the null
+ * terminator. For WriteUtf8: The number of bytes copied to the buffer
+ * including the null terminator (if written).
+ */
+ enum WriteOptions {
+ NO_OPTIONS = 0,
+ HINT_MANY_WRITES_EXPECTED = 1,
+ NO_NULL_TERMINATION = 2,
+ PRESERVE_ASCII_NULL = 4
+ };
+
+ uint16_t GetCharacter(int index);
+
+ // 16-bit character codes.
+ int Write(uint16_t* buffer,
+ int start = 0,
+ int length = -1,
+ int options = NO_OPTIONS) const;
+
+ /**
+ * A zero length string.
+ */
+ static v8::Local<v8::String> Empty();
+ static v8::Local<v8::String> Empty(Isolate* isolate);
+
+ /**
+ * Returns true if the string is external
+ */
+ bool IsExternal() const;
+
+ class V8EXPORT ExternalStringResourceBase { // NOLINT
+ public:
+ virtual ~ExternalStringResourceBase() {}
+
+ protected:
+ ExternalStringResourceBase() {}
+
+ /**
+ * Internally V8 will call this Dispose method when the external string
+ * resource is no longer needed. The default implementation will use the
+ * delete operator. This method can be overridden in subclasses to
+ * control how allocated external string resources are disposed.
+ */
+ virtual void Dispose() { delete this; }
+
+ };
+
+ /**
+ * An ExternalStringResource is a wrapper around a two-byte string
+ * buffer that resides outside V8's heap. Implement an
+ * ExternalStringResource to manage the life cycle of the underlying
+ * buffer. Note that the string data must be immutable.
+ */
+ class V8EXPORT ExternalStringResource
+ : public ExternalStringResourceBase {
+ public:
+ /**
+ * Override the destructor to manage the life cycle of the underlying
+ * buffer.
+ */
+ virtual ~ExternalStringResource() {}
+
+ /**
+ * The string data from the underlying buffer.
+ */
+ virtual const uint16_t* data() const = 0;
+
+ /**
+ * The length of the string. That is, the number of two-byte characters.
+ */
+ virtual size_t length() const = 0;
+
+ protected:
+ ExternalStringResource() {}
+ };
+
+ /**
+ * Get the ExternalStringResource for an external string. Returns
+ * NULL if IsExternal() doesn't return true.
+ */
+ ExternalStringResource* GetExternalStringResource() const;
+
+ static String* Cast(v8::Value* obj);
+
+ /**
+ * Allocates a new string from either UTF-8 encoded or ASCII data.
+ * The second parameter 'length' gives the buffer length.
+ * If the data is UTF-8 encoded, the caller must
+ * be careful to supply the length parameter.
+ * If it is not given, the function calls
+ * 'strlen' to determine the buffer length, it might be
+ * wrong if 'data' contains a null character.
+ */
+ static Local<String> New(const char* data, int length = -1);
+
+ /** Allocates a new string from 16-bit character codes.*/
+ static Local<String> New(const uint16_t* data, int length = -1);
+
+ /** Creates a symbol. Returns one if it exists already.*/
+ static Local<String> NewSymbol(const char* data, int length = -1);
+
+ static Local<String> New(QQmlJS::VM::String *s);
+
+ /**
+ * Creates a new external string using the data defined in the given
+ * resource. When the external string is no longer live on V8's heap the
+ * resource will be disposed by calling its Dispose method. The caller of
+ * this function should not otherwise delete or modify the resource. Neither
+ * should the underlying buffer be deallocated or modified except through the
+ * destructor of the external string resource.
+ */
+ static Local<String> NewExternal(ExternalStringResource* resource);
+
+ /**
+ * Converts an object to an ASCII string.
+ * Useful if you want to print the object.
+ * If conversion to a string fails (eg. due to an exception in the toString()
+ * method of the object) then the length() method returns 0 and the * operator
+ * returns NULL.
+ */
+ class V8EXPORT AsciiValue {
+ public:
+ explicit AsciiValue(Handle<v8::Value> obj);
+ ~AsciiValue() {}
+ char* operator*() { return str.data(); }
+ const char* operator*() const { return str.constData(); }
+ int length() const { return str.length(); }
+ private:
+ QByteArray str;
+
+ // Disallow copying and assigning.
+ AsciiValue(const AsciiValue&);
+ void operator=(const AsciiValue&);
+ };
+
+ /**
+ * Converts an object to a two-byte string.
+ * If conversion to a string fails (eg. due to an exception in the toString()
+ * method of the object) then the length() method returns 0 and the * operator
+ * returns NULL.
+ */
+ class V8EXPORT Value {
+ public:
+ explicit Value(Handle<v8::Value> obj);
+ ~Value() {}
+ uint16_t* operator*() { return (uint16_t *)str.data(); }
+ const uint16_t* operator*() const { return str.utf16(); }
+ int length() const { return str.length(); }
+ private:
+ QString str;
+
+ // Disallow copying and assigning.
+ Value(const Value&);
+ void operator=(const Value&);
+ };
+
+ QString asQString() const;
+ QQmlJS::VM::String *asVMString() const;
+};
+
+
+/**
+ * A JavaScript number value (ECMA-262, 4.3.20)
+ */
+class V8EXPORT Number : public Primitive {
+ public:
+ double Value() const;
+ static Local<Number> New(double value);
+ static Number* Cast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a signed integer.
+ */
+class V8EXPORT Integer : public Number {
+ public:
+ static Local<Integer> New(int32_t value);
+ static Local<Integer> NewFromUnsigned(uint32_t value);
+ static Local<Integer> New(int32_t value, Isolate*);
+ static Local<Integer> NewFromUnsigned(uint32_t value, Isolate*);
+ int64_t Value() const;
+ static Integer* Cast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit signed integer.
+ */
+class V8EXPORT Int32 : public Integer {
+ public:
+ int32_t Value() const;
+ private:
+ Int32();
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit unsigned integer.
+ */
+class V8EXPORT Uint32 : public Integer {
+ public:
+ uint32_t Value() const;
+ private:
+ Uint32();
+};
+
+
+enum PropertyAttribute {
+ None = 0,
+ ReadOnly = 1 << 0,
+ DontEnum = 1 << 1,
+ DontDelete = 1 << 2
+};
+
+/**
+ * Accessor[Getter|Setter] are used as callback functions when
+ * setting|getting a particular property. See Object and ObjectTemplate's
+ * method SetAccessor.
+ */
+typedef Handle<Value> (*AccessorGetter)(Local<String> property,
+ const AccessorInfo& info);
+
+
+typedef void (*AccessorSetter)(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info);
+
+
+/**
+ * Access control specifications.
+ *
+ * Some accessors should be accessible across contexts. These
+ * accessors have an explicit access control parameter which specifies
+ * the kind of cross-context access that should be allowed.
+ *
+ * Additionally, for security, accessors can prohibit overwriting by
+ * accessors defined in JavaScript. For objects that have such
+ * accessors either locally or in their prototype chain it is not
+ * possible to overwrite the accessor by using __defineGetter__ or
+ * __defineSetter__ from JavaScript code.
+ */
+enum AccessControl {
+ DEFAULT = 0,
+ ALL_CAN_READ = 1,
+ ALL_CAN_WRITE = 1 << 1,
+ PROHIBITS_OVERWRITING = 1 << 2
+};
+
+
+/**
+ * A JavaScript object (ECMA-262, 4.3.3)
+ */
+class V8EXPORT Object : public Value {
+ public:
+ bool Set(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
+
+ bool Set(uint32_t index,
+ Handle<Value> value);
+
+ Local<Value> Get(Handle<Value> key);
+
+ Local<Value> Get(uint32_t index);
+
+ // TODO(1245389): Replace the type-specific versions of these
+ // functions with generic ones that accept a Handle<Value> key.
+ bool Has(Handle<String> key);
+
+ bool Delete(Handle<String> key);
+
+ bool Has(uint32_t index);
+
+ bool Delete(uint32_t index);
+
+ bool SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None);
+
+ /**
+ * Returns an array containing the names of the enumerable properties
+ * of this object, including properties from prototype objects. The
+ * array returned by this method contains the same values as would
+ * be enumerated by a for-in statement over this object.
+ */
+ Local<Array> GetPropertyNames();
+
+ /**
+ * This function has the same functionality as GetPropertyNames but
+ * the returned array doesn't contain the names of properties from
+ * prototype objects.
+ */
+ Local<Array> GetOwnPropertyNames();
+
+ /**
+ * Get the prototype object. This does not skip objects marked to
+ * be skipped by __proto__ and it does not consult the security
+ * handler.
+ */
+ Local<Value> GetPrototype();
+
+ /**
+ * Set the prototype object. This does not skip objects marked to
+ * be skipped by __proto__ and it does not consult the security
+ * handler.
+ */
+ bool SetPrototype(Handle<Value> prototype);
+
+ /** Gets the value in an internal field. */
+ Local<Value> GetInternalField(int index);
+ /** Sets the value in an internal field. */
+ void SetInternalField(int index, Handle<Value> value);
+
+ class V8EXPORT ExternalResource { // NOLINT
+ public:
+ ExternalResource() {}
+ virtual ~ExternalResource() {}
+
+ virtual void Dispose() { delete this; }
+
+ private:
+ // Disallow copying and assigning.
+ ExternalResource(const ExternalResource&);
+ void operator=(const ExternalResource&);
+ };
+
+ void SetExternalResource(ExternalResource *);
+ ExternalResource *GetExternalResource();
+
+ // Testers for local properties.
+ bool HasOwnProperty(Handle<String> key);
+
+ /**
+ * Returns the identity hash for this object. The current implementation
+ * uses a hidden property on the object to store the identity hash.
+ *
+ * The return value will never be 0. Also, it is not guaranteed to be
+ * unique.
+ */
+ int GetIdentityHash();
+
+ /**
+ * Access hidden properties on JavaScript objects. These properties are
+ * hidden from the executing JavaScript and only accessible through the V8
+ * C++ API. Hidden properties introduced by V8 internally (for example the
+ * identity hash) are prefixed with "v8::".
+ */
+ bool SetHiddenValue(Handle<String> key, Handle<Value> value);
+ Local<Value> GetHiddenValue(Handle<String> key);
+
+ /**
+ * Clone this object with a fast but shallow copy. Values will point
+ * to the same values as the original object.
+ */
+ Local<Object> Clone();
+
+
+ /**
+ * Checks whether a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ * When an Object is callable this method returns true.
+ */
+ bool IsCallable();
+
+ /**
+ * Call an Object as a function if a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ */
+ Local<Value> CallAsFunction(Handle<Object> recv,
+ int argc,
+ Handle<Value> argv[]);
+
+ /**
+ * Call an Object as a constructor if a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ * Note: This method behaves like the Function::NewInstance method.
+ */
+ Local<Value> CallAsConstructor(int argc,
+ Handle<Value> argv[]);
+
+ static Local<Object> New();
+ static Object* Cast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in array constructor (ECMA-262, 15.4.2).
+ */
+class V8EXPORT Array : public Object {
+ public:
+ uint32_t Length() const;
+
+ /**
+ * Creates a JavaScript array with the given length. If the length
+ * is negative the returned array will have length 0.
+ */
+ static Local<Array> New(int length = 0);
+
+ static Array* Cast(Value* obj);
+};
+
+
+/**
+ * A JavaScript function object (ECMA-262, 15.3).
+ */
+class V8EXPORT Function : public Object {
+ public:
+ Local<Object> NewInstance() const;
+ Local<Object> NewInstance(int argc, Handle<Value> argv[]) const;
+ Local<Value> Call(Handle<Object> recv,
+ int argc,
+ Handle<Value> argv[]);
+ Handle<Value> GetName() const;
+
+ ScriptOrigin GetScriptOrigin() const;
+ static Function* Cast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Date constructor (ECMA-262, 15.9).
+ */
+class V8EXPORT Date : public Object {
+ public:
+ static Local<Value> New(double time);
+
+ /**
+ * A specialization of Value::NumberValue that is more efficient
+ * because we know the structure of this object.
+ */
+ double NumberValue() const;
+
+ static Date* Cast(v8::Value* obj);
+
+ /**
+ * Notification that the embedder has changed the time zone,
+ * daylight savings time, or other date / time configuration
+ * parameters. V8 keeps a cache of various values used for
+ * date / time computation. This notification will reset
+ * those cached values for the current context so that date /
+ * time configuration changes would be reflected in the Date
+ * object.
+ *
+ * This API should not be called more than needed as it will
+ * negatively impact the performance of date operations.
+ */
+ static void DateTimeConfigurationChangeNotification();
+
+};
+
+
+/**
+ * A Number object (ECMA-262, 4.3.21).
+ */
+class V8EXPORT NumberObject : public Object {
+ public:
+ static Local<Value> New(double value);
+
+ /**
+ * Returns the Number held by the object.
+ */
+ double NumberValue() const;
+
+ static NumberObject* Cast(v8::Value* obj);
+
+};
+
+
+/**
+ * A Boolean object (ECMA-262, 4.3.15).
+ */
+class V8EXPORT BooleanObject : public Object {
+ public:
+ static Local<Value> New(bool value);
+
+ /**
+ * Returns the Boolean held by the object.
+ */
+ bool BooleanValue() const;
+
+ static BooleanObject* Cast(v8::Value* obj);
+
+};
+
+
+/**
+ * A String object (ECMA-262, 4.3.18).
+ */
+class V8EXPORT StringObject : public Object {
+ public:
+ static Local<Value> New(Handle<String> value);
+
+ /**
+ * Returns the String held by the object.
+ */
+ Local<String> StringValue() const;
+
+ static StringObject* Cast(v8::Value* obj);
+
+};
+
+
+/**
+ * An instance of the built-in RegExp constructor (ECMA-262, 15.10).
+ */
+class V8EXPORT RegExp : public Object {
+ public:
+ /**
+ * Regular expression flag bits. They can be or'ed to enable a set
+ * of flags.
+ */
+ enum Flags {
+ kNone = 0,
+ kGlobal = 1,
+ kIgnoreCase = 2,
+ kMultiline = 4
+ };
+
+ /**
+ * Creates a regular expression from the given pattern string and
+ * the flags bit field. May throw a JavaScript exception as
+ * described in ECMA-262, 15.10.4.1.
+ *
+ * For example,
+ * RegExp::New(v8::String::New("foo"),
+ * static_cast<RegExp::Flags>(kGlobal | kMultiline))
+ * is equivalent to evaluating "/foo/gm".
+ */
+ static Local<RegExp> New(Handle<String> pattern,
+ Flags flags);
+
+ /**
+ * Returns the value of the source property: a string representing
+ * the regular expression.
+ */
+ Local<String> GetSource() const;
+
+ /**
+ * Returns the flags bit field.
+ */
+ Flags GetFlags() const;
+
+ static RegExp* Cast(v8::Value* obj);
+
+};
+
+
+/**
+ * A JavaScript value that wraps a C++ void*. This type of value is
+ * mainly used to associate C++ data structures with JavaScript
+ * objects.
+ *
+ * The Wrap function V8 will return the most optimal Value object wrapping the
+ * C++ void*. The type of the value is not guaranteed to be an External object
+ * and no assumptions about its type should be made. To access the wrapped
+ * value Unwrap should be used, all other operations on that object will lead
+ * to unpredictable results.
+ */
+class V8EXPORT External : public Value {
+ public:
+ static Local<Value> Wrap(void* data);
+ static void* Unwrap(Handle<Value> obj);
+
+ static Local<External> New(void* value);
+ static External* Cast(Value* obj);
+ void* Value() const;
+};
+
+
+// --- Templates ---
+
+
+/**
+ * The superclass of object and function templates.
+ */
+class V8EXPORT Template : public Data {
+ public:
+ /** Adds a property to each instance created by this template.*/
+ void Set(Handle<String> name, Handle<Value> value,
+ PropertyAttribute attributes = None);
+ void Set(const char* name, Handle<Value> value);
+
+ struct Property {
+ Persistent<String> name;
+ Persistent<Value> value;
+ PropertyAttribute attributes;
+ };
+ QVector<Property> m_properties;
+ };
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Template)
+
+/**
+ * The argument information given to function call callbacks. This
+ * class provides access to information about the context of the call,
+ * including the receiver, the number and values of arguments, and
+ * the holder of the function.
+ */
+class V8EXPORT Arguments {
+ public:
+ Arguments(const QQmlJS::VM::Value *args, int argc, const QQmlJS::VM::Value &thisObject, bool isConstructor,
+ const Persistent<Value> &data);
+ int Length() const;
+ Local<Value> operator[](int i) const;
+ Local<Object> This() const;
+ Local<Object> Holder() const;
+ bool IsConstructCall() const;
+ Local<Value> Data() const;
+ Isolate* GetIsolate() const;
+
+private:
+ QVector<Persistent<Value> > m_args;
+ Persistent<Object> m_thisObject;
+ bool m_isConstructor;
+ Persistent<Value> m_data;
+};
+
+
+/**
+ * The information passed to an accessor callback about the context
+ * of the property access.
+ */
+class V8EXPORT AccessorInfo {
+ public:
+ AccessorInfo(const QQmlJS::VM::Value &thisObject, const Persistent<Value> &data);
+ Isolate* GetIsolate() const;
+ Local<Value> Data() const;
+ Local<Object> This() const;
+ Local<Object> Holder() const;
+private:
+ Persistent<Value> m_this;
+ Persistent<Value> m_data;
+};
+
+
+typedef Handle<Value> (*InvocationCallback)(const Arguments& args);
+
+/**
+ * NamedProperty[Getter|Setter] are used as interceptors on object.
+ * See ObjectTemplate::SetNamedPropertyHandler.
+ */
+typedef Handle<Value> (*NamedPropertyGetter)(Local<String> property,
+ const AccessorInfo& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*NamedPropertySetter)(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info);
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes (like v8::None,
+ * v8::DontEnum, etc.)
+ */
+typedef Handle<Integer> (*NamedPropertyQuery)(Local<String> property,
+ const AccessorInfo& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef Handle<Boolean> (*NamedPropertyDeleter)(Local<String> property,
+ const AccessorInfo& info);
+
+/**
+ * Returns an array containing the names of the properties the named
+ * property getter intercepts.
+ */
+typedef Handle<Array> (*NamedPropertyEnumerator)(const AccessorInfo& info);
+
+
+/**
+ * Returns the value of the property if the getter intercepts the
+ * request. Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*IndexedPropertyGetter)(uint32_t index,
+ const AccessorInfo& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*IndexedPropertySetter)(uint32_t index,
+ Local<Value> value,
+ const AccessorInfo& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes.
+ */
+typedef Handle<Integer> (*IndexedPropertyQuery)(uint32_t index,
+ const AccessorInfo& info);
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef Handle<Boolean> (*IndexedPropertyDeleter)(uint32_t index,
+ const AccessorInfo& info);
+
+/**
+ * Returns an array containing the indices of the properties the
+ * indexed property getter intercepts.
+ */
+typedef Handle<Array> (*IndexedPropertyEnumerator)(const AccessorInfo& info);
+
+
+/**
+ * A FunctionTemplate is used to create functions at runtime. There
+ * can only be one function created from a FunctionTemplate in a
+ * context. The lifetime of the created function is equal to the
+ * lifetime of the context. So in case the embedder needs to create
+ * temporary functions that can be collected using Scripts is
+ * preferred.
+ *
+ * A FunctionTemplate can have properties, these properties are added to the
+ * function object when it is created.
+ *
+ * A FunctionTemplate has a corresponding instance template which is
+ * used to create object instances when the function is used as a
+ * constructor. Properties added to the instance template are added to
+ * each object instance.
+ *
+ * A FunctionTemplate can have a prototype template. The prototype template
+ * is used to create the prototype object of the function.
+ *
+ * The following example shows how to use a FunctionTemplate:
+ *
+ * \code
+ * v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
+ * t->Set("func_property", v8::Number::New(1));
+ *
+ * v8::Local<v8::Template> proto_t = t->PrototypeTemplate();
+ * proto_t->Set("proto_method", v8::FunctionTemplate::New(InvokeCallback));
+ * proto_t->Set("proto_const", v8::Number::New(2));
+ *
+ * v8::Local<v8::ObjectTemplate> instance_t = t->InstanceTemplate();
+ * instance_t->SetAccessor("instance_accessor", InstanceAccessorCallback);
+ * instance_t->SetNamedPropertyHandler(PropertyHandlerCallback, ...);
+ * instance_t->Set("instance_property", Number::New(3));
+ *
+ * v8::Local<v8::Function> function = t->GetFunction();
+ * v8::Local<v8::Object> instance = function->NewInstance();
+ * \endcode
+ *
+ * Let's use "function" as the JS variable name of the function object
+ * and "instance" for the instance object created above. The function
+ * and the instance will have the following properties:
+ *
+ * \code
+ * func_property in function == true;
+ * function.func_property == 1;
+ *
+ * function.prototype.proto_method() invokes 'InvokeCallback'
+ * function.prototype.proto_const == 2;
+ *
+ * instance instanceof function == true;
+ * instance.instance_accessor calls 'InstanceAccessorCallback'
+ * instance.instance_property == 3;
+ * \endcode
+ *
+ * A FunctionTemplate can inherit from another one by calling the
+ * FunctionTemplate::Inherit method. The following graph illustrates
+ * the semantics of inheritance:
+ *
+ * \code
+ * FunctionTemplate Parent -> Parent() . prototype -> { }
+ * ^ ^
+ * | Inherit(Parent) | .__proto__
+ * | |
+ * FunctionTemplate Child -> Child() . prototype -> { }
+ * \endcode
+ *
+ * A FunctionTemplate 'Child' inherits from 'Parent', the prototype
+ * object of the Child() function has __proto__ pointing to the
+ * Parent() function's prototype object. An instance of the Child
+ * function has all properties on Parent's instance templates.
+ *
+ * Let Parent be the FunctionTemplate initialized in the previous
+ * section and create a Child FunctionTemplate by:
+ *
+ * \code
+ * Local<FunctionTemplate> parent = t;
+ * Local<FunctionTemplate> child = FunctionTemplate::New();
+ * child->Inherit(parent);
+ *
+ * Local<Function> child_function = child->GetFunction();
+ * Local<Object> child_instance = child_function->NewInstance();
+ * \endcode
+ *
+ * The Child function and Child instance will have the following
+ * properties:
+ *
+ * \code
+ * child_func.prototype.__proto__ == function.prototype;
+ * child_instance.instance_accessor calls 'InstanceAccessorCallback'
+ * child_instance.instance_property == 3;
+ * \endcode
+ */
+class V8EXPORT FunctionTemplate : public Template {
+ public:
+ /** Creates a function template.*/
+ static Local<FunctionTemplate> New(
+ InvocationCallback callback = 0,
+ Handle<Value> data = Handle<Value>());
+ /** Returns the unique function instance in the current execution context.*/
+ Local<Function> GetFunction();
+
+ /** Get the InstanceTemplate. */
+ Local<ObjectTemplate> InstanceTemplate();
+
+ /**
+ * A PrototypeTemplate is the template used to create the prototype object
+ * of the function created by this template.
+ */
+ Local<ObjectTemplate> PrototypeTemplate();
+
+private:
+ FunctionTemplate(InvocationCallback callback, Handle<Value> data);
+ friend class V4V8Function;
+ InvocationCallback m_callback;
+ Persistent<Value> m_data;
+ Local<ObjectTemplate> m_instanceTemplate;
+ Local<ObjectTemplate> m_prototypeTemplate;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(FunctionTemplate)
+
+
+/**
+ * An ObjectTemplate is used to create objects at runtime.
+ *
+ * Properties added to an ObjectTemplate are added to each object
+ * created from the ObjectTemplate.
+ */
+class V8EXPORT ObjectTemplate : public Template {
+ public:
+ /** Creates an ObjectTemplate. */
+ static Local<ObjectTemplate> New();
+
+ /** Creates a new instance of this template.*/
+ Local<Object> NewInstance();
+
+ /**
+ * Sets an accessor on the object template.
+ *
+ * Whenever the property with the given name is accessed on objects
+ * created from this ObjectTemplate the getter and setter callbacks
+ * are called instead of getting and setting the property directly
+ * on the JavaScript object.
+ *
+ * \param name The name of the property for which an accessor is added.
+ * \param getter The callback to invoke when getting the property.
+ * \param setter The callback to invoke when setting the property.
+ * \param data A piece of data that will be passed to the getter and setter
+ * callbacks whenever they are invoked.
+ * \param settings Access control settings for the accessor. This is a bit
+ * field consisting of one of more of
+ * DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
+ * The default is to not allow cross-context access.
+ * ALL_CAN_READ means that all cross-context reads are allowed.
+ * ALL_CAN_WRITE means that all cross-context writes are allowed.
+ * The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
+ * cross-context access.
+ * \param attribute The attributes of the property for which an accessor
+ * is added.
+ * \param signature The signature describes valid receivers for the accessor
+ * and is used to perform implicit instance checks against them. If the
+ * receiver is incompatible (i.e. is not an instance of the constructor as
+ * defined by FunctionTemplate::HasInstance()), an implicit TypeError is
+ * thrown and no callback is invoked.
+ */
+ void SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None);
+
+ /**
+ * Sets a named property handler on the object template.
+ *
+ * Whenever a named property is accessed on objects created from
+ * this object template, the provided callback is invoked instead of
+ * accessing the property directly on the JavaScript object.
+ *
+ * \param getter The callback to invoke when getting a property.
+ * \param setter The callback to invoke when setting a property.
+ * \param query The callback to invoke to check if a property is present,
+ * and if present, get its attributes.
+ * \param deleter The callback to invoke when deleting a property.
+ * \param enumerator The callback to invoke to enumerate all the named
+ * properties of an object.
+ * \param data A piece of data that will be passed to the callbacks
+ * whenever they are invoked.
+ */
+ void SetNamedPropertyHandler(NamedPropertyGetter getter,
+ NamedPropertySetter setter = 0,
+ NamedPropertyQuery query = 0,
+ NamedPropertyDeleter deleter = 0,
+ NamedPropertyEnumerator enumerator = 0,
+ Handle<Value> data = Handle<Value>());
+ void SetFallbackPropertyHandler(NamedPropertyGetter getter,
+ NamedPropertySetter setter = 0,
+ NamedPropertyQuery query = 0,
+ NamedPropertyDeleter deleter = 0,
+ NamedPropertyEnumerator enumerator = 0,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Sets an indexed property handler on the object template.
+ *
+ * Whenever an indexed property is accessed on objects created from
+ * this object template, the provided callback is invoked instead of
+ * accessing the property directly on the JavaScript object.
+ *
+ * \param getter The callback to invoke when getting a property.
+ * \param setter The callback to invoke when setting a property.
+ * \param query The callback to invoke to check if an object has a property.
+ * \param deleter The callback to invoke when deleting a property.
+ * \param enumerator The callback to invoke to enumerate all the indexed
+ * properties of an object.
+ * \param data A piece of data that will be passed to the callbacks
+ * whenever they are invoked.
+ */
+ void SetIndexedPropertyHandler(IndexedPropertyGetter getter,
+ IndexedPropertySetter setter = 0,
+ IndexedPropertyQuery query = 0,
+ IndexedPropertyDeleter deleter = 0,
+ IndexedPropertyEnumerator enumerator = 0,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Gets the number of internal fields for objects generated from
+ * this template.
+ */
+ int InternalFieldCount();
+
+ /**
+ * Sets the number of internal fields for objects generated from
+ * this template.
+ */
+ void SetInternalFieldCount(int value);
+
+ /**
+ * Sets whether the object can store an "external resource" object.
+ */
+ bool HasExternalResource();
+ void SetHasExternalResource(bool value);
+
+ /**
+ * Mark object instances of the template as using the user object
+ * comparison callback.
+ */
+ void MarkAsUseUserObjectComparison();
+
+ struct Accessor {
+ Persistent<Value> getter;
+ Persistent<Value> setter;
+ Persistent<String> name;
+ PropertyAttribute attribute;
+ };
+
+ QVector<Accessor> m_accessors;
+
+ NamedPropertyGetter m_namedPropertyGetter;
+ NamedPropertySetter m_namedPropertySetter;
+ NamedPropertyQuery m_namedPropertyQuery;
+ NamedPropertyDeleter m_namedPropertyDeleter;
+ NamedPropertyEnumerator m_namedPropertyEnumerator;
+ Persistent<Value> m_namedPropertyData;
+
+ NamedPropertyGetter m_fallbackPropertyGetter;
+ NamedPropertySetter m_fallbackPropertySetter;
+ NamedPropertyQuery m_fallbackPropertyQuery;
+ NamedPropertyDeleter m_fallbackPropertyDeleter;
+ NamedPropertyEnumerator m_fallbackPropertyEnumerator;
+ Persistent<Value> m_fallbackPropertyData;
+
+ IndexedPropertyGetter m_indexedPropertyGetter;
+ IndexedPropertySetter m_indexedPropertySetter;
+ IndexedPropertyQuery m_indexedPropertyQuery;
+ IndexedPropertyDeleter m_indexedPropertyDeleter;
+ IndexedPropertyEnumerator m_indexedPropertyEnumerator;
+ Persistent<Value> m_indexedPropertyData;
+
+ bool m_useUserComparison;
+ private:
+ ObjectTemplate();
+ };
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(ObjectTemplate)
+
+// --- Statics ---
+
+
+Handle<Primitive> V8EXPORT Undefined();
+Handle<Primitive> V8EXPORT Null();
+Handle<Boolean> V8EXPORT True();
+Handle<Boolean> V8EXPORT False();
+
+inline Handle<Primitive> Undefined(Isolate*) { return Undefined(); }
+inline Handle<Primitive> Null(Isolate*) { return Null(); }
+inline Handle<Boolean> True(Isolate*) { return True(); }
+inline Handle<Boolean> False(Isolate*) { return False(); }
+
+
+
+// --- Exceptions ---
+
+
+/**
+ * Schedules an exception to be thrown when returning to JavaScript. When an
+ * exception has been scheduled it is illegal to invoke any JavaScript
+ * operation; the caller must return immediately and only after the exception
+ * has been handled does it become legal to invoke JavaScript operations.
+ */
+Handle<Value> V8EXPORT ThrowException(Handle<Value> exception);
+
+/**
+ * Create new error objects by calling the corresponding error object
+ * constructor with the message.
+ */
+class V8EXPORT Exception {
+ public:
+ static Local<Value> ReferenceError(Handle<String> message);
+ static Local<Value> SyntaxError(Handle<String> message);
+ static Local<Value> TypeError(Handle<String> message);
+ static Local<Value> Error(Handle<String> message);
+};
+
+
+// --- User Object Comparison Callback ---
+typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs,
+ Local<Object> rhs);
+
+// --- Garbage Collection Callbacks ---
+
+/**
+ * Applications can register callback functions which will be called
+ * before and after a garbage collection. Allocations are not
+ * allowed in the callback functions, you therefore cannot manipulate
+ * objects (set or delete properties for example) since it is possible
+ * such operations will result in the allocation of objects.
+ */
+enum GCType {
+ kGCTypeScavenge = 1 << 0,
+ kGCTypeMarkSweepCompact = 1 << 1,
+ kGCTypeAll = kGCTypeScavenge | kGCTypeMarkSweepCompact
+};
+
+enum GCCallbackFlags {
+ kNoGCCallbackFlags = 0,
+ kGCCallbackFlagCompacted = 1 << 0
+};
+
+typedef void (*GCPrologueCallback)(GCType type, GCCallbackFlags flags);
+typedef void (*GCCallback)();
+
+
+
+/**
+ * Isolate represents an isolated instance of the V8 engine. V8
+ * isolates have completely separate states. Objects from one isolate
+ * must not be used in other isolates. When V8 is initialized a
+ * default isolate is implicitly created and entered. The embedder
+ * can create additional isolates and use them in parallel in multiple
+ * threads. An isolate can be entered by at most one thread at any
+ * given time. The Locker/Unlocker API must be used to synchronize.
+ */
+class V8EXPORT Isolate {
+ public:
+ Isolate();
+ ~Isolate();
+ /**
+ * Stack-allocated class which sets the isolate for all operations
+ * executed within a local scope.
+ */
+ class V8EXPORT Scope {
+ public:
+ explicit Scope(Isolate* isolate) : isolate_(isolate) {
+ isolate->Enter();
+ }
+
+ ~Scope() { isolate_->Exit(); }
+
+ private:
+ Isolate* const isolate_;
+
+ // Prevent copying of Scope objects.
+ Scope(const Scope&);
+ Scope& operator=(const Scope&);
+ };
+
+ /**
+ * Creates a new isolate. Does not change the currently entered
+ * isolate.
+ *
+ * When an isolate is no longer used its resources should be freed
+ * by calling Dispose(). Using the delete operator is not allowed.
+ */
+ static Isolate* New();
+
+ /**
+ * Returns the entered isolate for the current thread or NULL in
+ * case there is no current isolate.
+ */
+ static Isolate* GetCurrent();
+
+ /**
+ * Methods below this point require holding a lock (using Locker) in
+ * a multi-threaded environment.
+ */
+
+ /**
+ * Sets this isolate as the entered one for the current thread.
+ * Saves the previously entered one (if any), so that it can be
+ * restored when exiting. Re-entering an isolate is allowed.
+ */
+ void Enter();
+
+ /**
+ * Exits this isolate by restoring the previously entered one in the
+ * current thread. The isolate may still stay the same, if it was
+ * entered more than once.
+ *
+ * Requires: this == Isolate::GetCurrent().
+ */
+ void Exit();
+
+ /**
+ * Disposes the isolate. The isolate must not be entered by any
+ * thread to be disposable.
+ */
+ void Dispose();
+
+ /**
+ * Associate embedder-specific data with the isolate
+ */
+ void SetData(void* data);
+
+ /**
+ * Retrieve embedder-specific data from the isolate.
+ * Returns NULL if SetData has never been called.
+ */
+ void* GetData();
+
+ Context *GetCurrentContext() { return m_contextStack.top(); }
+ void setException(const QQmlJS::VM::Value &ex);
+
+ private:
+ friend class Context;
+ friend class TryCatch;
+ Isolate* m_lastIsolate;
+ QStack<Context*> m_contextStack;
+ TryCatch *tryCatch;
+};
+
+
+/**
+ * Container class for static utility functions.
+ */
+class V8EXPORT V8 {
+ public:
+
+ /**
+ * Sets V8 flags from a string.
+ */
+ static void SetFlagsFromString(const char* str, int length);
+
+ /** Callback for user object comparisons */
+ static void SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback);
+
+ /**
+ * Enables the host application to receive a notification before a
+ * garbage collection. Allocations are not allowed in the
+ * callback function, you therefore cannot manipulate objects (set
+ * or delete properties for example) since it is possible such
+ * operations will result in the allocation of objects. It is possible
+ * to specify the GCType filter for your callback. But it is not possible to
+ * register the same callback function two times with different
+ * GCType filters.
+ */
+ static void AddGCPrologueCallback(
+ GCPrologueCallback callback, GCType gc_type_filter = kGCTypeAll);
+
+ /**
+ * This function removes callback which was installed by
+ * AddGCPrologueCallback function.
+ */
+ static void RemoveGCPrologueCallback(GCPrologueCallback callback);
+
+ /**
+ * Allows the host application to declare implicit references between
+ * the objects: if |parent| is alive, all |children| are alive too.
+ * After each garbage collection, all implicit references
+ * are removed. It is intended to be used in the before-garbage-collection
+ * callback function.
+ */
+ static void AddImplicitReferences(Persistent<Object> parent,
+ Persistent<Value>* children,
+ size_t length);
+
+ /**
+ * Initializes from snapshot if possible. Otherwise, attempts to
+ * initialize from scratch. This function is called implicitly if
+ * you use the API without calling it first.
+ */
+ static bool Initialize();
+
+ /**
+ * Releases any resources used by v8 and stops any utility threads
+ * that may be running. Note that disposing v8 is permanent, it
+ * cannot be reinitialized.
+ *
+ * It should generally not be necessary to dispose v8 before exiting
+ * a process, this should happen automatically. It is only necessary
+ * to use if the process needs the resources taken up by v8.
+ */
+ static bool Dispose();
+
+ /**
+ * Optional notification that the embedder is idle.
+ * V8 uses the notification to reduce memory footprint.
+ * This call can be used repeatedly if the embedder remains idle.
+ * Returns true if the embedder should stop calling IdleNotification
+ * until real work has been done. This indicates that V8 has done
+ * as much cleanup as it will be able to do.
+ *
+ * The hint argument specifies the amount of work to be done in the function
+ * on scale from 1 to 1000. There is no guarantee that the actual work will
+ * match the hint.
+ */
+ static bool IdleNotification(int hint = 1000);
+
+ /**
+ * Optional notification that the system is running low on memory.
+ * V8 uses these notifications to attempt to free memory.
+ */
+ static void LowMemoryNotification();
+};
+
+/**
+ * An external exception handler.
+ */
+class V8EXPORT TryCatch {
+ public:
+ /**
+ * Creates a new try/catch block and registers it with v8.
+ */
+ TryCatch();
+
+ /**
+ * Unregisters and deletes this try/catch block.
+ */
+ ~TryCatch();
+
+ /**
+ * Returns true if an exception has been caught by this try/catch block.
+ */
+ bool HasCaught() const;
+
+ /**
+ * Throws the exception caught by this TryCatch in a way that avoids
+ * it being caught again by this same TryCatch. As with ThrowException
+ * it is illegal to execute any JavaScript operations after calling
+ * ReThrow; the caller must return immediately to where the exception
+ * is caught.
+ */
+ Handle<Value> ReThrow();
+
+ /**
+ * Returns the exception caught by this try/catch block. If no exception has
+ * been caught an empty handle is returned.
+ *
+ * The returned handle is valid until this TryCatch block has been destroyed.
+ */
+ Local<Value> Exception() const;
+
+ /**
+ * Returns the message associated with this exception. If there is
+ * no message associated an empty handle is returned.
+ *
+ * The returned handle is valid until this TryCatch block has been
+ * destroyed.
+ */
+ Local<v8::Message> Message() const;
+
+ /**
+ * Clears any exceptions that may have been caught by this try/catch block.
+ * After this method has been called, HasCaught() will return false.
+ *
+ * It is not necessary to clear a try/catch block before using it again; if
+ * another exception is thrown the previously caught exception will just be
+ * overwritten. However, it is often a good idea since it makes it easier
+ * to determine which operation threw a given exception.
+ */
+ void Reset();
+
+private:
+ friend class Isolate;
+ TryCatch *parent;
+ bool hasCaughtException;
+ Local<Value> exception;
+};
+
+
+// --- Context ---
+class V8EXPORT ExtensionConfiguration;
+
+/**
+ * A sandboxed execution context with its own set of built-in objects
+ * and functions.
+ */
+class V8EXPORT Context : public QSharedData {
+ public:
+ Context();
+ ~Context();
+
+ static Local<Context> Adopt(Context *p)
+ {
+ Local<Context> l;
+ l.object = p;
+ l.object->ref.ref();
+ return l;
+ }
+ /**
+ * Returns the global proxy object or global object itself for
+ * detached contexts.
+ *
+ * Global proxy object is a thin wrapper whose prototype points to
+ * actual context's global object with the properties like Object, etc.
+ * This is done that way for security reasons (for more details see
+ * https://wiki.mozilla.org/Gecko:SplitWindow).
+ *
+ * Please note that changes to global proxy object prototype most probably
+ * would break VM---v8 expects only global object as a prototype of
+ * global proxy object.
+ *
+ * If DetachGlobal() has been invoked, Global() would return actual global
+ * object until global is reattached with ReattachGlobal().
+ */
+ Local<Object> Global();
+
+ /** Creates a new context.
+ *
+ * Returns a persistent handle to the newly allocated context. This
+ * persistent handle has to be disposed when the context is no
+ * longer used so the context can be garbage collected.
+ *
+ * \param extensions An optional extension configuration containing
+ * the extensions to be installed in the newly created context.
+ *
+ * \param global_template An optional object template from which the
+ * global object for the newly created context will be created.
+ *
+ * \param global_object An optional global object to be reused for
+ * the newly created context. This global object must have been
+ * created by a previous call to Context::New with the same global
+ * template. The state of the global object will be completely reset
+ * and only object identify will remain.
+ */
+ static Persistent<Context> New(
+ ExtensionConfiguration* extensions = NULL,
+ Handle<ObjectTemplate> global_template = Handle<ObjectTemplate>(),
+ Handle<Value> global_object = Handle<Value>());
+
+ /** Returns the context that is on the top of the stack. */
+ static Local<Context> GetCurrent();
+
+ /**
+ * Returns the context of the calling JavaScript code. That is the
+ * context of the top-most JavaScript frame. If there are no
+ * JavaScript frames an empty handle is returned.
+ */
+ static Local<Context> GetCalling();
+ static Local<Object> GetCallingQmlGlobal();
+ static Local<Value> GetCallingScriptData();
+
+ /**
+ * Enter this context. After entering a context, all code compiled
+ * and run is compiled and run in this context. If another context
+ * is already entered, this old context is saved so it can be
+ * restored when the new context is exited.
+ */
+ void Enter();
+
+ /**
+ * Exit this context. Exiting the current context restores the
+ * context that was in place when entering the current context.
+ */
+ void Exit();
+
+ /**
+ * Associate an additional data object with the context. This is mainly used
+ * with the debugger to provide additional information on the context through
+ * the debugger API.
+ */
+ void SetData(Handle<Value> data);
+ Local<Value> GetData();
+
+ /**
+ * Stack-allocated class which sets the execution context for all
+ * operations executed within a local scope.
+ */
+ class Scope {
+ public:
+ explicit Scope(Handle<Context> context) : context_(context) {
+ context_->Enter();
+ }
+ ~Scope() { context_->Exit(); }
+ private:
+ Handle<Context> context_;
+ };
+
+ QQmlJS::VM::ExecutionEngine *GetEngine();
+
+private:
+ Context* m_lastContext;
+ struct Private;
+ Private *d;
+ friend class Value;
+ friend class Script;
+ friend class Object;
+ friend class Function;
+};
+
+DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Context)
+
+template<typename T>
+void Persistent<T>::MakeWeak(void* parameters, WeakReferenceCallback callback)
+{
+ Q_UNUSED(parameters);
+ Q_UNUSED(callback);
+
+ Q_UNIMPLEMENTED();
+}
+
+
+
+} // namespace v8
+
+
+#undef V8EXPORT
+#undef TYPE_CHECK
+
+
+#endif // V8_H_
diff --git a/src/qml/qml/v4vm/qv4value.cpp b/src/qml/qml/v4vm/qv4value.cpp
new file mode 100644
index 0000000000..73efe7d22d
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4value.cpp
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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.h>
+#include <qv4object.h>
+#include <qv4objectproto.h>
+#include "qv4mm.h"
+
+#include <wtf/MathExtras.h>
+
+namespace QQmlJS {
+namespace VM {
+
+
+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);
+}
+
+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));
+}
+
+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 (isnan(number))
+ return +0;
+ else if (! number || isinf(number))
+ return number;
+ const double v = floor(fabs(number));
+ return std::signbit(number) ? -v : v;
+}
+
+Value Value::property(ExecutionContext *ctx, String *name) const
+{
+ return isObject() ? objectValue()->get(ctx, name) : undefinedValue();
+}
+
+PersistentValue::PersistentValue()
+ : m_memoryManager(0)
+ , m_value(Value::undefinedValue())
+{
+}
+
+PersistentValue::PersistentValue(MemoryManager *mm, const Value &val)
+ : m_memoryManager(mm)
+ , m_value(val)
+{
+ assert(mm);
+ if (Managed *m = asManaged())
+ m_memoryManager->protect(m);
+}
+
+PersistentValue::PersistentValue(const PersistentValue &other)
+ : m_memoryManager(other.m_memoryManager)
+ , m_value(other.m_value)
+{
+ if (Managed *m = asManaged())
+ m_memoryManager->protect(m);
+}
+
+PersistentValue &PersistentValue::operator=(const PersistentValue &other)
+{
+ if (this == &other)
+ return *this;
+ if (Managed *m = asManaged())
+ m_memoryManager->unprotect(m);
+ m_memoryManager = other.m_memoryManager;
+ m_value = other.m_value;
+ if (Managed *m = asManaged())
+ m_memoryManager->protect(m);
+}
+
+PersistentValue::~PersistentValue()
+{
+ if (Managed *m = asManaged())
+ m_memoryManager->unprotect(m);
+}
+
+
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/qml/qml/v4vm/qv4value.h b/src/qml/qml/v4vm/qv4value.h
new file mode 100644
index 0000000000..bbfba842b5
--- /dev/null
+++ b/src/qml/qml/v4vm/qv4value.h
@@ -0,0 +1,572 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the V4VM module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and 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 <QtCore/QString>
+#include <QtCore/qnumeric.h>
+#include "qv4global.h"
+#include "qv4string.h"
+#include <QtCore/QDebug>
+#include "qv4managed.h"
+
+#include <wtf/MathExtras.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct String;
+struct ExecutionContext;
+struct ExecutionEngine;
+struct Value;
+
+extern "C" {
+double __qmljs_to_number(const Value &value);
+Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value);
+Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value);
+}
+
+typedef uint Bool;
+
+
+struct Q_V4_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,
+ _Deleted_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 isDeleted() const { return tag == _Deleted_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; }
+
+ 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 deletedValue();
+ 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);
+#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;
+ 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;
+ RegExpObject *asRegExpObject() const;
+ ArrayObject *asArrayObject() const;
+ ErrorObject *asErrorObject() const;
+ uint asArrayIndex() const;
+ uint asArrayLength(bool *ok) const;
+
+ Value property(ExecutionContext *ctx, String *name) const;
+
+ // Section 9.12
+ bool sameValue(Value other) const;
+
+ void 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 VM::Value Value::deletedValue()
+{
+ VM::Value v;
+ v.tag = VM::Value::_Deleted_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() || isnan(doubleValue()))
+ return false;
+ return true;
+ }
+}
+
+inline String *Value::toString(ExecutionContext *ctx) const
+{
+ if (isString())
+ return stringValue();
+ return __qmljs_convert_to_string(ctx, *this);
+}
+
+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 RegExpObject *Value::asRegExpObject() const
+{
+ return isObject() ? managed()->asRegExpObject() : 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(ExecutionContext *context, Value *args, int argc) {
+ return vtbl->construct(this, context, args, argc);
+}
+inline Value Managed::call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) {
+ return vtbl->call(this, context, thisObject, args, argc);
+}
+
+class PersistentValue
+{
+public:
+ PersistentValue();
+ PersistentValue(MemoryManager *mm, const Value &val);
+ PersistentValue(const PersistentValue &other);
+ PersistentValue &operator=(const PersistentValue &other);
+ ~PersistentValue();
+
+ Value *operator->() { return &m_value; }
+ Value *operator*() { return &m_value; }
+
+ operator Value() const { return m_value; }
+
+private:
+ Managed *asManaged() { return m_memoryManager ? m_value.asManaged() : 0; }
+ MemoryManager *m_memoryManager;
+ Value m_value;
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/qml/v4vm/v4.pri b/src/qml/qml/v4vm/v4.pri
new file mode 100644
index 0000000000..3b9711bdb6
--- /dev/null
+++ b/src/qml/qml/v4vm/v4.pri
@@ -0,0 +1,8 @@
+include(llvm_installation.pri)
+include(../3rdparty/masm/masm-defs.pri)
+
+CONFIG += exceptions
+
+!llvm: DEFINES += QMLJS_NO_LLVM
+
+INCLUDEPATH += $$PWD
diff --git a/src/qml/qml/v4vm/v4.pro b/src/qml/qml/v4vm/v4.pro
new file mode 100644
index 0000000000..325a1013b1
--- /dev/null
+++ b/src/qml/qml/v4vm/v4.pro
@@ -0,0 +1,177 @@
+TARGET = QtV4
+QT_PRIVATE = core-private qmldevtools-private
+QT = core
+
+CONFIG += internal_module
+
+include(v4.pri)
+
+load(qt_build_config)
+load(qt_module)
+
+CONFIG += warn_off
+
+#win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??!
+
+!macx-clang*:!win*:LIBS += -rdynamic
+
+SOURCES += \
+ qv4codegen.cpp \
+ qv4jsir.cpp \
+ qv4engine.cpp \
+ qv4context.cpp \
+ qv4runtime.cpp \
+ qv4value.cpp \
+ qv4syntaxchecker.cpp \
+ qv4isel_masm.cpp \
+ llvm_runtime.cpp \
+ qv4isel_p.cpp \
+ debugging.cpp \
+ qv4lookup.cpp \
+ qv4mm.cpp \
+ qv4managed.cpp \
+ qv4internalclass.cpp \
+ qv4sparsearray.cpp \
+ qv4arrayobject.cpp \
+ qv4argumentsobject.cpp \
+ qv4booleanobject.cpp \
+ qv4dateobject.cpp \
+ qv4errorobject.cpp \
+ qv4functionobject.cpp \
+ qv4globalobject.cpp \
+ qv4jsonobject.cpp \
+ qv4mathobject.cpp \
+ qv4numberobject.cpp \
+ qv4object.cpp \
+ qv4objectproto.cpp \
+ qv4regexpobject.cpp \
+ qv4stringobject.cpp \
+ qv4string.cpp \
+ qv4objectiterator.cpp \
+ qv4regexp.cpp \
+ qv4unwindhelper.cpp \
+ qv4v8.cpp \
+ qv4executableallocator.cpp
+
+HEADERS += \
+ qv4global.h \
+ qv4codegen_p.h \
+ qv4jsir_p.h \
+ qv4engine.h \
+ qv4context.h \
+ qv4runtime.h \
+ qv4math.h \
+ qv4value.h \
+ qv4syntaxchecker_p.h \
+ qv4isel_masm_p.h \
+ qv4isel_p.h \
+ qv4isel_util_p.h \
+ debugging.h \
+ qv4lookup.h \
+ qv4identifier.h \
+ qv4mm.h \
+ qv4managed.h \
+ qv4internalclass.h \
+ qv4sparsearray.h \
+ qv4arrayobject.h \
+ qv4argumentsobject.h \
+ qv4booleanobject.h \
+ qv4dateobject.h \
+ qv4errorobject.h \
+ qv4functionobject.h \
+ qv4globalobject.h \
+ qv4jsonobject.h \
+ qv4mathobject.h \
+ qv4numberobject.h \
+ qv4object.h \
+ qv4objectproto.h \
+ qv4regexpobject.h \
+ qv4stringobject.h \
+ qv4string.h \
+ qv4property.h \
+ qv4objectiterator.h \
+ qv4regexp.h \
+ qv4unwindhelper.h \
+ qv4unwindhelper_p-dw2.h \
+ qv4unwindhelper_p-arm.h \
+ qv4v8.h \
+ qcalculatehash_p.h \
+ qv4util.h \
+ qv4executableallocator.h
+
+llvm-libs {
+
+SOURCES += \
+ qv4isel_llvm.cpp
+
+HEADERS += \
+ qv4isel_llvm_p.h \
+ 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
+}
+
+TESTSCRIPT=$$PWD/../../tests/test262.py
+V4CMD = v4
+
+checktarget.target = check
+checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations
+checktarget.depends = all
+QMAKE_EXTRA_TARGETS += checktarget
+
+checkmothtarget.target = check-interpreter
+checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations
+checkmothtarget.depends = all
+QMAKE_EXTRA_TARGETS += checkmothtarget
+
+linux*|mac {
+ LIBS += -ldl
+}
+
+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
+}
+
+include(moth/moth.pri)
+include(../3rdparty/masm/masm.pri)
+include(../3rdparty/double-conversion/double-conversion.pri)