aboutsummaryrefslogtreecommitdiffstats
path: root/src/v4
diff options
context:
space:
mode:
Diffstat (limited to 'src/v4')
-rw-r--r--src/v4/debugging.cpp214
-rw-r--r--src/v4/debugging.h141
-rw-r--r--src/v4/llvm_runtime.cpp523
-rw-r--r--src/v4/moth/moth.pri13
-rw-r--r--src/v4/moth/qv4instr_moth.cpp15
-rw-r--r--src/v4/moth/qv4instr_moth_p.h514
-rw-r--r--src/v4/moth/qv4isel_moth.cpp975
-rw-r--r--src/v4/moth/qv4isel_moth_p.h168
-rw-r--r--src/v4/moth/qv4vme_moth.cpp483
-rw-r--r--src/v4/moth/qv4vme_moth_p.h37
-rw-r--r--src/v4/qmljs_engine.cpp493
-rw-r--r--src/v4/qmljs_engine.h235
-rw-r--r--src/v4/qmljs_environment.cpp564
-rw-r--r--src/v4/qmljs_environment.h150
-rw-r--r--src/v4/qmljs_math.h106
-rw-r--r--src/v4/qmljs_runtime.cpp1161
-rw-r--r--src/v4/qmljs_runtime.h862
-rw-r--r--src/v4/qmljs_value.cpp163
-rw-r--r--src/v4/qmljs_value.h480
-rw-r--r--src/v4/qv4_llvm_p.h53
-rw-r--r--src/v4/qv4argumentsobject.cpp161
-rw-r--r--src/v4/qv4argumentsobject.h85
-rw-r--r--src/v4/qv4array.cpp649
-rw-r--r--src/v4/qv4array.h639
-rw-r--r--src/v4/qv4arrayobject.cpp798
-rw-r--r--src/v4/qv4arrayobject.h95
-rw-r--r--src/v4/qv4booleanobject.cpp94
-rw-r--r--src/v4/qv4booleanobject.h72
-rw-r--r--src/v4/qv4codegen.cpp2614
-rw-r--r--src/v4/qv4codegen_p.h421
-rw-r--r--src/v4/qv4dateobject.cpp1313
-rw-r--r--src/v4/qv4dateobject.h125
-rw-r--r--src/v4/qv4errorobject.cpp257
-rw-r--r--src/v4/qv4errorobject.h206
-rw-r--r--src/v4/qv4functionobject.cpp465
-rw-r--r--src/v4/qv4functionobject.h229
-rw-r--r--src/v4/qv4global.h49
-rw-r--r--src/v4/qv4globalobject.cpp670
-rw-r--r--src/v4/qv4globalobject.h84
-rw-r--r--src/v4/qv4ir.cpp670
-rw-r--r--src/v4/qv4ir_p.h724
-rw-r--r--src/v4/qv4isel_llvm.cpp1388
-rw-r--r--src/v4/qv4isel_llvm_p.h177
-rw-r--r--src/v4/qv4isel_masm.cpp933
-rw-r--r--src/v4/qv4isel_masm_p.h798
-rw-r--r--src/v4/qv4isel_p.cpp414
-rw-r--r--src/v4/qv4isel_p.h144
-rw-r--r--src/v4/qv4isel_util_p.h63
-rw-r--r--src/v4/qv4jsonobject.cpp935
-rw-r--r--src/v4/qv4jsonobject.h61
-rw-r--r--src/v4/qv4managed.cpp141
-rw-r--r--src/v4/qv4managed.h173
-rw-r--r--src/v4/qv4mathobject.cpp299
-rw-r--r--src/v4/qv4mathobject.h78
-rw-r--r--src/v4/qv4mm.cpp396
-rw-r--r--src/v4/qv4mm.h128
-rw-r--r--src/v4/qv4numberobject.cpp233
-rw-r--r--src/v4/qv4numberobject.h76
-rw-r--r--src/v4/qv4object.cpp674
-rw-r--r--src/v4/qv4object.h192
-rw-r--r--src/v4/qv4objectiterator.cpp166
-rw-r--r--src/v4/qv4objectiterator.h79
-rw-r--r--src/v4/qv4objectproto.cpp558
-rw-r--r--src/v4/qv4objectproto.h95
-rw-r--r--src/v4/qv4propertydescriptor.h169
-rw-r--r--src/v4/qv4propertytable.h224
-rw-r--r--src/v4/qv4regexp.cpp76
-rw-r--r--src/v4/qv4regexp.h92
-rw-r--r--src/v4/qv4regexpobject.cpp237
-rw-r--r--src/v4/qv4regexpobject.h100
-rw-r--r--src/v4/qv4string.cpp124
-rw-r--r--src/v4/qv4string.h98
-rw-r--r--src/v4/qv4stringobject.cpp722
-rw-r--r--src/v4/qv4stringobject.h100
-rw-r--r--src/v4/qv4syntaxchecker.cpp119
-rw-r--r--src/v4/qv4syntaxchecker_p.h73
-rw-r--r--src/v4/v4.pro155
77 files changed, 28260 insertions, 0 deletions
diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp
new file mode 100644
index 0000000000..61ae0e62d0
--- /dev/null
+++ b/src/v4/debugging.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 Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#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)
+{
+ if (idx < _context->argumentCount)
+ return _context->arguments + idx;
+ else
+ return 0;
+}
+
+VM::Value *FunctionState::local(unsigned idx)
+{
+ if (idx < _context->variableCount())
+ return _context->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(IR::Function *function)
+{
+ _functionInfo.insert(function, new FunctionDebugInfo(function));
+}
+
+void Debugger::setSourceLocation(IR::Function *function, unsigned line, unsigned column)
+{
+ _functionInfo[function]->setSourceLocation(line, column);
+}
+
+void Debugger::mapFunction(VM::Function *vmf, IR::Function *irf)
+{
+ _vmToIr.insert(vmf, irf);
+}
+
+FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const
+{
+ if (!function)
+ return 0;
+
+ if (VM::ScriptFunction *sf = function->asScriptFunction())
+ return _functionInfo[irFunction(sf->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(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;
+}
+
+IR::Function *Debugger::irFunction(VM::Function *vmf) const
+{
+ return _vmToIr[vmf];
+}
diff --git a/src/v4/debugging.h b/src/v4/debugging.h
new file mode 100644
index 0000000000..00c96717a5
--- /dev/null
+++ b/src/v4/debugging.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef DEBUGGING_H
+#define DEBUGGING_H
+
+#include "qv4global.h"
+#include "qmljs_engine.h"
+#include "qmljs_environment.h"
+
+#include <QHash>
+
+namespace QQmlJS {
+
+namespace IR {
+struct BasicBlock;
+struct Function;
+} // namespace IR
+
+namespace Debugging {
+
+class Debugger;
+
+struct FunctionDebugInfo { // TODO: use opaque d-pointers here
+ QString name;
+ unsigned startLine, startColumn;
+
+ FunctionDebugInfo(IR::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(IR::Function *function);
+ void setSourceLocation(IR::Function *function, unsigned line, unsigned column);
+ void mapFunction(VM::Function *vmf, IR::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(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);
+ IR::Function *irFunction(VM::Function *vmf) const;
+
+private: // TODO: use opaque d-pointers here
+ VM::ExecutionEngine *_engine;
+ QHash<IR::Function *, FunctionDebugInfo *> _functionInfo;
+ QHash<VM::Function *, IR::Function *> _vmToIr;
+ QVector<CallInfo> _callStack;
+};
+
+} // namespace Debugging
+} // namespace QQmlJS
+
+#endif // DEBUGGING_H
diff --git a/src/v4/llvm_runtime.cpp b/src/v4/llvm_runtime.cpp
new file mode 100644
index 0000000000..212c8d17d5
--- /dev/null
+++ b/src/v4/llvm_runtime.cpp
@@ -0,0 +1,523 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qmljs_environment.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)
+{
+ return &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(__qmljs_string_from_utf8(ctx, 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);
+ *result = __qmljs_init_closure(clos, ctx);
+}
+
+bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value)
+{
+ return __qmljs_to_boolean(*value, ctx);
+}
+
+void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_bit_and(*left, *right, ctx);
+}
+
+void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_bit_or(*left, *right, ctx);
+}
+
+void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_bit_xor(*left, *right, ctx);
+}
+
+void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_add(*left, *right, ctx);
+}
+
+void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_sub(*left, *right, ctx);
+}
+
+void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_mul(*left, *right, ctx);
+}
+
+void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_div(*left, *right, ctx);
+}
+
+void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_mod(*left, *right, ctx);
+}
+
+void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_shl(*left, *right, ctx);
+}
+
+void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_shr(*left, *right, ctx);
+}
+
+void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_ushr(*left, *right, ctx);
+}
+
+void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_gt(*left, *right, ctx);
+}
+
+void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_lt(*left, *right, ctx);
+}
+
+void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_ge(*left, *right, ctx);
+}
+
+void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_le(*left, *right, ctx);
+}
+
+void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_eq(*left, *right, ctx);
+}
+
+void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_ne(*left, *right, ctx);
+}
+
+void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_se(*left, *right, ctx);
+}
+
+void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_sne(*left, *right, ctx);
+}
+
+void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_instanceof(*left, *right, ctx);
+}
+
+void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right)
+{
+ *result = __qmljs_in(*left, *right, ctx);
+}
+
+void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ *result = __qmljs_uplus(*value, ctx);
+}
+
+void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ *result = __qmljs_uminus(*value, ctx);
+}
+
+void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ *result = __qmljs_compl(*value, ctx);
+}
+
+void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value)
+{
+ *result = __qmljs_not(*value, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_and_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_or_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_bit_xor_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_add_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_sub_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_mul_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_div_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_mod_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_shl_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_shr_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src)
+{
+ __qmljs_inplace_ushr_name(*src, dest, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_and_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_or_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_bit_xor_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_add_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_sub_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_mul_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_div_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_mod_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_shl_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_shr_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value)
+{
+ __qmljs_inplace_ushr_element(*base, *index, *value, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_and_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_or_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_bit_xor_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_add_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_sub_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_mul_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_div_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_mod_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_shl_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_shr_member(*value, *base, member, ctx);
+}
+
+void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member)
+{
+ __qmljs_inplace_ushr_member(*value, *base, member, ctx);
+}
+
+String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str)
+{
+ return __qmljs_identifier_from_utf8(ctx, str); // ### make it unique
+}
+
+void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ *result = __qmljs_call_activation_property(context, name, args, argc);
+}
+
+void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc)
+{
+ Value that = thisObject ? *thisObject : Value::undefinedValue();
+ *result = __qmljs_call_value(context, that, *func, args, argc);
+}
+
+void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc)
+{
+ *result = __qmljs_construct_activation_property(context, name, args, argc);
+}
+
+void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc)
+{
+ *result = __qmljs_construct_value(context, *func, args, argc);
+}
+
+void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name)
+{
+ *result = __qmljs_get_activation_property(ctx, 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)
+{
+ *result = __qmljs_get_property(ctx, *object, name);
+}
+
+void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc)
+{
+ *result = __qmljs_call_property(context, *base, name, args, argc);
+}
+
+void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc)
+{
+ *result = __qmljs_construct_property(context, *base, name, args, argc);
+}
+
+void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index)
+{
+ *result = __qmljs_get_element(ctx, *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)
+{
+ *result = __qmljs_builtin_typeof(*value, ctx);
+}
+
+void __qmljs_llvm_throw(ExecutionContext *context, Value *value)
+{
+ __qmljs_throw(*value, context);
+}
+
+void __qmljs_llvm_create_exception_handler(ExecutionContext *context, Value *result)
+{
+ void *buf = __qmljs_create_exception_handler(context);
+ *result = Value::fromInt32(setjmp(* static_cast<jmp_buf *>(buf)));
+}
+
+void __qmljs_llvm_delete_exception_handler(ExecutionContext *context)
+{
+ __qmljs_delete_exception_handler(context);
+}
+
+void __qmljs_llvm_get_exception(ExecutionContext *context, Value *result)
+{
+ *result = __qmljs_get_exception(context);
+}
+
+void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in)
+{
+ *result = __qmljs_foreach_iterator_object(*in, context);
+}
+
+void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it)
+{
+ *result = __qmljs_foreach_next_property_name(*it);
+}
+
+void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result)
+{
+ *result = __qmljs_get_thisObject(ctx);
+}
+
+void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index)
+{
+ *result = __qmljs_delete_subscript(ctx, *base, *index);
+}
+
+void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name)
+{
+ *result = __qmljs_delete_member(ctx, *base, name);
+}
+
+void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name)
+{
+ *result = __qmljs_delete_name(ctx, name);
+}
+
+} // extern "C"
diff --git a/src/v4/moth/moth.pri b/src/v4/moth/moth.pri
new file mode 100644
index 0000000000..73bd893286
--- /dev/null
+++ b/src/v4/moth/moth.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qv4isel_moth_p.h \
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4vme_moth_p.h
+
+SOURCES += \
+ $$PWD/qv4isel_moth.cpp \
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4vme_moth.cpp
+
+#DEFINES += DO_TRACE_INSTR
diff --git a/src/v4/moth/qv4instr_moth.cpp b/src/v4/moth/qv4instr_moth.cpp
new file mode 100644
index 0000000000..a2bad39e00
--- /dev/null
+++ b/src/v4/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/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h
new file mode 100644
index 0000000000..741ddba79d
--- /dev/null
+++ b/src/v4/moth/qv4instr_moth_p.h
@@ -0,0 +1,514 @@
+#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(CallValue, callValue) \
+ F(CallProperty, callProperty) \
+ F(CallElement, callElement) \
+ F(CallActivationProperty, callActivationProperty) \
+ F(CallBuiltinThrow, callBuiltinThrow) \
+ F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \
+ F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \
+ F(CallBuiltinGetException, callBuiltinGetException) \
+ 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(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \
+ 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
+ };
+ VM::Value value;
+ unsigned type : 2;
+ unsigned index : 30;
+
+ bool isValue() const { return type == ValueType; }
+ bool isArgument() const { return type == ArgumentType; }
+ bool isLocal() const { return type == LocalType; }
+ bool isTemp() const { return type == TempType; }
+
+ static Param createValue(const VM::Value &v)
+ {
+ Param p;
+ p.type = ValueType;
+ p.value = v;
+ return p;
+ }
+
+ static Param createArgument(unsigned idx)
+ {
+ Param p;
+ p.type = ArgumentType;
+ p.index = idx;
+ return p;
+ }
+
+ static Param createLocal(unsigned idx)
+ {
+ Param p;
+ p.type = LocalType;
+ p.index = idx;
+ return p;
+ }
+
+ static Param createTemp(unsigned idx)
+ {
+ Param p;
+ p.type = TempType;
+ 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_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_callBuiltinCreateExceptionHandler {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ struct instr_callBuiltinDeleteExceptionHandler {
+ MOTH_INSTR_HEADER
+ };
+ struct instr_callBuiltinGetException {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ 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_callBuiltinDefineArrayProperty {
+ MOTH_INSTR_HEADER
+ Param object;
+ Param value;
+ int index;
+ };
+ 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::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx);
+ Param source;
+ Param result;
+ };
+ struct instr_binop {
+ MOTH_INSTR_HEADER
+ VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *);
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_loadThis {
+ MOTH_INSTR_HEADER
+ Param result;
+ };
+ struct instr_inplaceElementOp {
+ MOTH_INSTR_HEADER
+ void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *);
+ Param base;
+ Param index;
+ Param source;
+ };
+ struct instr_inplaceMemberOp {
+ MOTH_INSTR_HEADER
+ void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *);
+ VM::String *member;
+ Param base;
+ Param source;
+ };
+ struct instr_inplaceNameOp {
+ MOTH_INSTR_HEADER
+ void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *);
+ 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_callValue callValue;
+ instr_callProperty callProperty;
+ instr_callElement callElement;
+ instr_callActivationProperty callActivationProperty;
+ instr_callBuiltinThrow callBuiltinThrow;
+ instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler;
+ instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler;
+ instr_callBuiltinGetException callBuiltinGetException;
+ 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_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty;
+ 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/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp
new file mode 100644
index 0000000000..5522be4c32
--- /dev/null
+++ b/src/v4/moth/qv4isel_moth.cpp
@@ -0,0 +1,975 @@
+#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 {
+
+QTextStream qout(stderr, QIODevice::WriteOnly);
+
+//#define 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 IR::StmtVisitor, IR::ExprVisitor
+{
+public:
+ void run(IR::Function *function)
+ {
+ DBTC(qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps.";)
+
+ _seenTemps.clear();
+ _nextFree = 0;
+ _active.reserve(function->tempCount);
+ _localCount = function->locals.size();
+
+ QVector<int> pinned;
+ foreach (IR::BasicBlock *block, function->basicBlocks) {
+ if (IR::Stmt *last = block->terminator()) {
+ const QBitArray &liveOut = last->d->liveOut;
+ for (int i = 0, 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 (IR::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;
+ }
+
+private:
+ virtual void visitConst(IR::Const *) {}
+ virtual void visitString(IR::String *) {}
+ virtual void visitRegExp(IR::RegExp *) {}
+ virtual void visitName(IR::Name *) {}
+ virtual void visitClosure(IR::Closure *) {}
+ virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(IR::Member *e) { e->base->accept(this); }
+ virtual void visitExp(IR::Exp *s) { s->expr->accept(this); }
+ virtual void visitEnter(IR::Enter *) {}
+ virtual void visitLeave(IR::Leave *) {}
+ virtual void visitJump(IR::Jump *) {}
+ virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
+
+ virtual void visitTemp(IR::Temp *e) {
+ if (_seenTemps.contains(e))
+ return;
+ _seenTemps.insert(e);
+
+ if (e->index < 0)
+ return;
+ if (e->index < _localCount) // don't optimise locals yet.
+ return;
+
+ e->index = remap(e->index - _localCount) + _localCount;
+ }
+
+ virtual void visitCall(IR::Call *e) {
+ e->base->accept(this);
+ for (IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitNew(IR::New *e) {
+ e->base->accept(this);
+ for (IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitMove(IR::Move *s) {
+ s->target->accept(this);
+ s->source->accept(this);
+ }
+
+ 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;
+ QSet<IR::Temp *> _seenTemps;
+ IR::Stmt *_currentStatement;
+ int _localCount;
+ int _nextFree;
+ int _pinnedCount;
+};
+#undef DBTC
+
+typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*);
+inline ALUFunction aluOpFunction(IR::AluOp op)
+{
+ switch (op) {
+ case IR::OpInvalid:
+ return 0;
+ case IR::OpIfTrue:
+ return 0;
+ case IR::OpNot:
+ return 0;
+ case IR::OpUMinus:
+ return 0;
+ case IR::OpUPlus:
+ return 0;
+ case IR::OpCompl:
+ return 0;
+ case IR::OpBitAnd:
+ return VM::__qmljs_bit_and;
+ case IR::OpBitOr:
+ return VM::__qmljs_bit_or;
+ case IR::OpBitXor:
+ return VM::__qmljs_bit_xor;
+ case IR::OpAdd:
+ return VM::__qmljs_add;
+ case IR::OpSub:
+ return VM::__qmljs_sub;
+ case IR::OpMul:
+ return VM::__qmljs_mul;
+ case IR::OpDiv:
+ return VM::__qmljs_div;
+ case IR::OpMod:
+ return VM::__qmljs_mod;
+ case IR::OpLShift:
+ return VM::__qmljs_shl;
+ case IR::OpRShift:
+ return VM::__qmljs_shr;
+ case IR::OpURShift:
+ return VM::__qmljs_ushr;
+ case IR::OpGt:
+ return VM::__qmljs_gt;
+ case IR::OpLt:
+ return VM::__qmljs_lt;
+ case IR::OpGe:
+ return VM::__qmljs_ge;
+ case IR::OpLe:
+ return VM::__qmljs_le;
+ case IR::OpEqual:
+ return VM::__qmljs_eq;
+ case IR::OpNotEqual:
+ return VM::__qmljs_ne;
+ case IR::OpStrictEqual:
+ return VM::__qmljs_se;
+ case IR::OpStrictNotEqual:
+ return VM::__qmljs_sne;
+ case IR::OpInstanceof:
+ return VM::__qmljs_instanceof;
+ case IR::OpIn:
+ return VM::__qmljs_in;
+ case IR::OpAnd:
+ return 0;
+ case IR::OpOr:
+ return 0;
+ default:
+ assert(!"Unknown AluOp");
+ return 0;
+ }
+};
+
+} // anonymous namespace
+
+InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::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, IR::Function *function)
+{
+ IR::BasicBlock *block;
+
+ QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches;
+ QHash<IR::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);
+
+ // TODO: FIXME: fix the temp compression with the new temp index layout.
+// CompressTemps().run(_function);
+
+ 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 (IR::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(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ Instruction::CallValue call;
+ prepareCallArgs(args, call.argc, call.args);
+ call.dest = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::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(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::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(IR::Name *func,
+ IR::ExprList *args,
+ IR::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(IR::Temp *base, const QString &name, IR::ExprList *args, IR::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(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ Instruction::CreateValue create;
+ create.func = getParam(value);
+ prepareCallArgs(args, create.argc, create.args);
+ create.result = getResultParam(result);
+ addInstruction(create);
+}
+
+void InstructionSelection::loadThisObject(IR::Temp *temp)
+{
+ Instruction::LoadThis load;
+ load.result = getResultParam(temp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp)
+{
+ assert(sourceConst);
+
+ Instruction::LoadValue load;
+ load.value = getParam(sourceConst);
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp)
+{
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str)));
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp)
+{
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(
+ VM::Value::fromObject(engine()->newRegExpObject(
+ *sourceRegexp->value,
+ sourceRegexp->flags)));
+ load.result = getResultParam(targetTemp);
+ addInstruction(load);
+}
+
+void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp)
+{
+ Instruction::LoadName load;
+ load.name = identifier(name);
+ load.result = getResultParam(temp);
+ addInstruction(load);
+}
+
+void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
+{
+ Instruction::StoreName store;
+ store.source = getParam(source);
+ store.name = identifier(targetName);
+ addInstruction(store);
+}
+
+void InstructionSelection::initClosure(IR::Closure *closure, IR::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(IR::Temp *base, const QString &name, IR::Temp *target)
+{
+ Instruction::LoadProperty load;
+ load.base = getParam(base);
+ load.name = identifier(name);
+ load.result = getResultParam(target);
+ addInstruction(load);
+}
+
+void InstructionSelection::setProperty(IR::Expr *source, IR::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(IR::Temp *base, IR::Temp *index, IR::Temp *target)
+{
+ Instruction::LoadElement load;
+ load.base = getParam(base);
+ load.index = getParam(index);
+ load.result = getResultParam(target);
+ addInstruction(load);
+}
+
+void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex)
+{
+ Instruction::StoreElement store;
+ store.base = getParam(targetBase);
+ store.index = getParam(targetIndex);
+ store.source = getParam(source);
+ addInstruction(store);
+}
+
+void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ Instruction::MoveTemp move;
+ move.source = getParam(sourceTemp);
+ move.result = getResultParam(targetTemp);
+ addInstruction(move);
+}
+
+void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0;
+ switch (oper) {
+ case IR::OpIfTrue: assert(!"unreachable"); break;
+ case IR::OpNot: op = VM::__qmljs_not; break;
+ case IR::OpUMinus: op = VM::__qmljs_uminus; break;
+ case IR::OpUPlus: op = VM::__qmljs_uplus; break;
+ case IR::OpCompl: op = VM::__qmljs_compl; break;
+ case IR::OpIncrement: op = VM::__qmljs_increment; break;
+ case IR::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(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::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(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName)
+{
+ void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0;
+ switch (oper) {
+ case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break;
+ case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break;
+ case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break;
+ case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break;
+ case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break;
+ case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break;
+ case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break;
+ case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break;
+ case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break;
+ case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break;
+ case IR::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(sourceExpr);
+ addInstruction(ieo);
+ }
+}
+
+void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp)
+{
+ void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0;
+ switch (oper) {
+ case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break;
+ case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break;
+ case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break;
+ case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break;
+ case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break;
+ case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break;
+ case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break;
+ case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break;
+ case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break;
+ case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break;
+ case IR::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(sourceExpr);
+ addInstruction(ieo);
+}
+
+void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName)
+{
+ void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0;
+ switch (oper) {
+ case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break;
+ case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break;
+ case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break;
+ case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break;
+ case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break;
+ case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break;
+ case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break;
+ case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break;
+ case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break;
+ case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break;
+ case IR::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(IR::ExprList *e, quint32 &argc, quint32 &args)
+{
+ bool singleArgIsTemp = false;
+ if (e && e->next == 0) {
+ // 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();
+ }
+
+ 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(IR::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(IR::CJump *s)
+{
+ Instr::Param condition;
+ if (IR::Temp *t = s->cond->asTemp()) {
+ condition = getResultParam(t);
+ } else if (IR::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(IR::Ret *s)
+{
+ Instruction::Ret ret;
+ ret.result = getParam(s->expr);
+ addInstruction(ret);
+}
+
+void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::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(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::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, IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result)
+{
+ Instruction::CallBuiltinTypeofValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ Instruction::CallBuiltinDeleteMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::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, IR::Temp *result)
+{
+ Instruction::CallBuiltinDeleteName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result)
+{
+ Instruction::LoadValue load;
+ load.value = Instr::Param::createValue(VM::Value::fromBoolean(false));
+ load.result = getResultParam(result);
+ addInstruction(load);
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::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, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostDecValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncMember call;
+ call.base = getParam(base);
+ call.member = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::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, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncName call;
+ call.name = identifier(name);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result)
+{
+ Instruction::CallBuiltinPostIncValue call;
+ call.value = getParam(value);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinThrow(IR::Temp *arg)
+{
+ Instruction::CallBuiltinThrow call;
+ call.arg = getParam(arg);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
+{
+ Instruction::CallBuiltinCreateExceptionHandler call;
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDeleteExceptionHandler()
+{
+ Instruction::CallBuiltinDeleteExceptionHandler call;
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinGetException(IR::Temp *result)
+{
+ Instruction::CallBuiltinGetException call;
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result)
+{
+ Instruction::CallBuiltinForeachIteratorObject call;
+ call.arg = getParam(arg);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result)
+{
+ Instruction::CallBuiltinForeachNextPropertyName call;
+ call.arg = getParam(arg);
+ call.result = getResultParam(result);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinPushWithScope(IR::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(IR::Temp *object, const QString &name, IR::Temp *getter, IR::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(IR::Temp *object, const QString &name, IR::Temp *value)
+{
+ Instruction::CallBuiltinDefineProperty call;
+ call.object = getParam(object);
+ call.name = identifier(name);
+ call.value = getParam(value);
+ addInstruction(call);
+}
+
+void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value)
+{
+ Instruction::CallBuiltinDefineArrayProperty call;
+ call.object = getParam(object);
+ call.index = index;
+ call.value = getParam(value);
+ 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<IR::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()->identifier(s);
+ _vmFunction->identifiers.append(str);
+ return str;
+}
diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h
new file mode 100644
index 0000000000..0b93ea853f
--- /dev/null
+++ b/src/v4/moth/qv4isel_moth_p.h
@@ -0,0 +1,168 @@
+#ifndef QV4ISEL_MOTH_P_H
+#define QV4ISEL_MOTH_P_H
+
+#include "qv4global.h"
+#include "qv4isel_p.h"
+#include "qv4ir_p.h"
+#include "qv4object.h"
+#include "qv4instr_moth_p.h"
+
+namespace QQmlJS {
+namespace Moth {
+
+class Q_V4_EXPORT InstructionSelection:
+ public IR::InstructionSelection,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(VM::Function *vmFunction, IR::Function *function);
+
+protected:
+ virtual void visitJump(IR::Jump *);
+ virtual void visitCJump(IR::CJump *);
+ virtual void visitRet(IR::Ret *);
+
+ 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 callBuiltinDeleteExceptionHandler();
+ virtual void callBuiltinGetException(IR::Temp *result);
+ 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 callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value);
+ 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 *sourceConst, IR::Temp *targetTemp);
+ 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::Expr *source, const QString &targetName);
+ virtual void initClosure(IR::Closure *closure, IR::Temp *target);
+ virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target);
+ virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target);
+ virtual void setElement(IR::Expr *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::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target);
+ virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName);
+ virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::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(IR::Expr *e)
+ {
+ Q_ASSERT(e);
+
+ typedef Instr::Param Param;
+ if (IR::Const *c = e->asConst()) {
+ return Param::createValue(convertToValue(c));
+ } else if (IR::Temp *t = e->asTemp()) {
+ const int index = t->index;
+ if (index < 0) {
+ return Param::createArgument(-index - 1);
+ } else {
+ const int localCount = _function->locals.size();
+ if (index < localCount)
+ return Param::createLocal(index);
+ else
+ return Param::createTemp(index - localCount);
+ }
+ } else {
+ Q_UNIMPLEMENTED();
+ return Param();
+ }
+ }
+
+ Instr::Param getResultParam(IR::Temp *result)
+ {
+ if (result)
+ return getParam(result);
+ else
+ return Instr::Param::createTemp(scratchTempIndex());
+ }
+
+ void simpleMove(IR::Move *);
+ void prepareCallArgs(IR::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);
+
+ IR::Function *_function;
+ VM::Function *_vmFunction;
+ IR::BasicBlock *_block;
+
+ QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
+ QHash<IR::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, IR::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/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp
new file mode 100644
index 0000000000..a3ab1de46a
--- /dev/null
+++ b/src/v4/moth/qv4vme_moth.cpp
@@ -0,0 +1,483 @@
+#include "qv4vme_moth_p.h"
+#include "qv4instr_moth_p.h"
+#include "qmljs_value.h"
+#include "debugging.h"
+
+#include <iostream>
+
+#include <alloca.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
+
+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\n");
+ } else if (param.isArgument()) {
+ fprintf(stderr, " argument %d\n", param.index);
+ } else if (param.isLocal()) {
+ fprintf(stderr, " local %d\n", param.index);
+ } else if (param.isTemp()) {
+ fprintf(stderr, " temp %d\n", param.index);
+ } else {
+ Q_ASSERT(!"INVALID");
+ }
+#endif // DO_TRACE_INSTR
+
+ if (param.isValue()) {
+ return const_cast<VM::Value *>(&param.value);
+ } else if (param.isArgument()) {
+ const unsigned arg = param.index;
+ Q_ASSERT(arg >= 0);
+ Q_ASSERT((unsigned) arg < context->argumentCount);
+ Q_ASSERT(context->arguments);
+ return context->arguments + arg;
+ } else if (param.isLocal()) {
+ const unsigned index = param.index;
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(index < context->variableCount());
+ Q_ASSERT(context->locals);
+ return context->locals + index;
+ } else if (param.isTemp()) {
+ Q_ASSERT(param.index < stackSize);
+ return stack + param.index;
+ } else {
+ Q_UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+#if defined(QT_NO_DEBUG)
+# define VALUE(param) *getValueRef(context, stack, param)
+# define VALUEPTR(param) 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::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code
+#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
+
+ VM::Value *stack = 0;
+ unsigned stackSize = 0;
+ 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)
+ VALUE(instr.result) = __qmljs_init_closure(instr.value, context);
+ MOTH_END_INSTR(LoadClosure)
+
+ MOTH_BEGIN_INSTR(LoadName)
+ TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData());
+ VALUE(instr.result) = __qmljs_get_activation_property(context, 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)
+ VALUE(instr.result) = __qmljs_get_element(context, 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)
+ VALUE(instr.result) = __qmljs_get_property(context, 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;
+ VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc);
+ MOTH_END_INSTR(CallValue)
+
+ MOTH_BEGIN_INSTR(CallProperty)
+ TRACE(property name, "%s", qPrintable(instr.name->toQString()));
+ Q_ASSERT(instr.args + instr.argc < stackSize);
+ VM::Value *args = stack + instr.args;
+ VALUE(instr.result) = __qmljs_call_property(context, 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;
+ VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc);
+ MOTH_END_INSTR(CallProperty)
+
+ MOTH_BEGIN_INSTR(CallActivationProperty)
+ Q_ASSERT(instr.args + instr.argc < stackSize);
+ VM::Value *args = stack + instr.args;
+ VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc);
+ MOTH_END_INSTR(CallActivationProperty)
+
+ MOTH_BEGIN_INSTR(CallBuiltinThrow)
+ __qmljs_builtin_throw(VALUE(instr.arg), context);
+ MOTH_END_INSTR(CallBuiltinThrow)
+
+ MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler)
+ void *buf = __qmljs_create_exception_handler(context);
+ // The result is the only value we need from the instr to
+ // continue execution when an exception is caught.
+ VM::Value *result = getValueRef(context, stack, instr.result
+#if !defined(QT_NO_DEBUG)
+ , stackSize
+#endif
+ );
+ int didThrow = setjmp(* static_cast<jmp_buf *>(buf));
+ // Two ways to come here: after a create, or after a throw.
+ if (didThrow)
+ // At this point, the interpreter state can be anything but
+ // valid, so first restore the state.
+ restoreState(context, result, code);
+ else
+ // Save the state and any variables we need when catching an
+ // exception, so we can restore the state at that point.
+ saveState(context, result, code);
+ *result = VM::Value::fromInt32(didThrow);
+ MOTH_END_INSTR(CallBuiltinCreateExceptionHandler)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler)
+ __qmljs_delete_exception_handler(context);
+ MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler)
+
+ MOTH_BEGIN_INSTR(CallBuiltinGetException)
+ VALUE(instr.result) = __qmljs_get_exception(context);
+ MOTH_END_INSTR(CallBuiltinGetException)
+
+ 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)
+ VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context);
+ MOTH_END_INSTR(CallBuiltinForeachIteratorObject)
+
+ MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName)
+ VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg));
+ MOTH_END_INSTR(CallBuiltinForeachNextPropertyName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteMember)
+ VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member);
+ MOTH_END_INSTR(CallBuiltinDeleteMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript)
+ VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index));
+ MOTH_END_INSTR(CallBuiltinDeleteSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDeleteName)
+ VALUE(instr.result) = __qmljs_delete_name(context, instr.name);
+ MOTH_END_INSTR(CallBuiltinDeleteName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofMember)
+ VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript)
+ VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context);
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofName)
+ VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinTypeofValue)
+ VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context);
+ MOTH_END_INSTR(CallBuiltinTypeofValue)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncMember)
+ VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript)
+ VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context);
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncName)
+ VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostIncValue)
+ VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context);
+ MOTH_END_INSTR(CallBuiltinTypeofValue)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecMember)
+ VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context);
+ MOTH_END_INSTR(CallBuiltinTypeofMember)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript)
+ VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context);
+ MOTH_END_INSTR(CallBuiltinTypeofSubscript)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecName)
+ VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context);
+ MOTH_END_INSTR(CallBuiltinTypeofName)
+
+ MOTH_BEGIN_INSTR(CallBuiltinPostDecValue)
+ VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context);
+ 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(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context);
+ MOTH_END_INSTR(CallBuiltinDefineGetterSetter)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineProperty)
+ __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context);
+ MOTH_END_INSTR(CallBuiltinDefineProperty)
+
+ MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty)
+ __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context);
+ MOTH_END_INSTR(CallBuiltinDefineArrayProperty)
+
+ MOTH_BEGIN_INSTR(CreateValue)
+ Q_ASSERT(instr.args + instr.argc < stackSize);
+ VM::Value *args = stack + instr.args;
+ VALUE(instr.result) = __qmljs_construct_value(context, 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;
+ VALUE(instr.result) = __qmljs_construct_property(context, 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;
+ VALUE(instr.result) = __qmljs_construct_activation_property(context, 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), context);
+ TRACE(condition, "%s", cond ? "TRUE" : "FALSE");
+ if (cond)
+ code = ((uchar *)&instr.offset) + instr.offset;
+ MOTH_END_INSTR(CJump)
+
+ MOTH_BEGIN_INSTR(Unop)
+ VALUE(instr.result) = instr.alu(VALUE(instr.source), context);
+ MOTH_END_INSTR(Unop)
+
+ MOTH_BEGIN_INSTR(Binop)
+ VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context);
+ 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) = __qmljs_get_thisObject(context);
+ MOTH_END_INSTR(LoadThis)
+
+ MOTH_BEGIN_INSTR(InplaceElementOp)
+ instr.alu(VALUE(instr.base),
+ VALUE(instr.index),
+ VALUE(instr.source),
+ context);
+ MOTH_END_INSTR(InplaceElementOp)
+
+ MOTH_BEGIN_INSTR(InplaceMemberOp)
+ instr.alu(VALUE(instr.source),
+ VALUE(instr.base),
+ instr.member,
+ context);
+ MOTH_END_INSTR(InplaceMemberOp)
+
+ MOTH_BEGIN_INSTR(InplaceNameOp)
+ TRACE(name, "%s", instr.name->toQString().toUtf8().constData());
+ instr.alu(VALUE(instr.source),
+ instr.name,
+ context);
+ 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) {
+ VME dummy;
+ dummy(0, 0, &jumpTable);
+ }
+ return jumpTable;
+}
+#endif
+
+VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code)
+{
+ VME vme;
+ return vme(ctxt, code);
+}
+
+void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code)
+{
+ VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
+ target = handler.target;
+ code = handler.code;
+}
+
+void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code)
+{
+ VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
+ handler.target = target;
+ handler.code = code;
+}
diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h
new file mode 100644
index 0000000000..2fd877f7b9
--- /dev/null
+++ b/src/v4/moth/qv4vme_moth_p.h
@@ -0,0 +1,37 @@
+#ifndef QV4VME_MOTH_P_H
+#define QV4VME_MOTH_P_H
+
+#include "qmljs_runtime.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 *);
+
+ VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code
+#ifdef MOTH_THREADED_INTERPRETER
+ , void ***storeJumpTable = 0
+#endif
+ );
+
+#ifdef MOTH_THREADED_INTERPRETER
+ static void **instructionJumpTable();
+#endif
+
+private:
+ static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code);
+ static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code);
+};
+
+} // namespace Moth
+} // namespace QQmlJS
+
+#endif // QV4VME_MOTH_P_H
diff --git a/src/v4/qmljs_engine.cpp b/src/v4/qmljs_engine.cpp
new file mode 100644
index 0000000000..0b1b14b13e
--- /dev/null
+++ b/src/v4/qmljs_engine.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 <qmljs_engine.h>
+#include <qmljs_value.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 <qmljs_runtime.h>
+#include "qv4mm.h"
+#include <qv4argumentsobject.h>
+#include <qv4dateobject.h>
+#include <qv4jsonobject.h>
+#include <qv4stringobject.h>
+
+namespace QQmlJS {
+namespace VM {
+
+ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
+ : memoryManager(new QQmlJS::VM::MemoryManager)
+ , iselFactory(factory)
+ , debugger(0)
+ , globalObject(Value::nullValue())
+ , globalCode(0)
+ , exception(Value::nullValue())
+{
+ MemoryManager::GCBlocker gcBlocker(memoryManager);
+
+ memoryManager->setExecutionEngine(this);
+
+ rootContext = newContext();
+ rootContext->init(this);
+ current = rootContext;
+
+ id_length = identifier(QStringLiteral("length"));
+ id_prototype = identifier(QStringLiteral("prototype"));
+ id_constructor = identifier(QStringLiteral("constructor"));
+ id_arguments = identifier(QStringLiteral("arguments"));
+ id_caller = identifier(QStringLiteral("caller"));
+ id_this = identifier(QStringLiteral("this"));
+ id___proto__ = identifier(QStringLiteral("__proto__"));
+ id_enumerable = identifier(QStringLiteral("enumerable"));
+ id_configurable = identifier(QStringLiteral("configurable"));
+ id_writable = identifier(QStringLiteral("writable"));
+ id_value = identifier(QStringLiteral("value"));
+ id_get = identifier(QStringLiteral("get"));
+ id_set = identifier(QStringLiteral("set"));
+ id_eval = identifier(QStringLiteral("eval"));
+
+ objectPrototype = new (memoryManager) ObjectPrototype();
+ stringPrototype = new (memoryManager) StringPrototype(rootContext);
+ numberPrototype = new (memoryManager) NumberPrototype();
+ booleanPrototype = new (memoryManager) BooleanPrototype();
+ arrayPrototype = new (memoryManager) ArrayPrototype(rootContext);
+ datePrototype = new (memoryManager) DatePrototype();
+ functionPrototype = new (memoryManager) FunctionPrototype(rootContext);
+ regExpPrototype = new (memoryManager) RegExpPrototype(this);
+ errorPrototype = new (memoryManager) ErrorPrototype(this);
+ 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
+ //
+ VM::Object *glo = newObject(/*rootContext*/);
+ globalObject = Value::fromObject(glo);
+ rootContext->activation = glo;
+ rootContext->thisObject = Value::fromObject(glo);
+
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(newMathObject(rootContext)));
+ glo->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext)));
+
+ glo->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue());
+ glo->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(nan("")));
+ glo->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(INFINITY));
+
+ evalFunction = new (memoryManager) EvalFunction(rootContext);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction));
+
+ glo->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
+ glo->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
+}
+
+ExecutionEngine::~ExecutionEngine()
+{
+ delete globalObject.asObject();
+ rootContext->destroy();
+ delete rootContext;
+ qDeleteAll(functions);
+ delete memoryManager;
+}
+
+ExecutionContext *ExecutionEngine::newContext()
+{
+ return new ExecutionContext();
+}
+
+String *ExecutionEngine::identifier(const QString &s)
+{
+ return new (memoryManager) String(s);
+}
+
+Function *ExecutionEngine::newFunction(const QString &name)
+{
+ VM::Function *f = new VM::Function(identifier(name));
+ functions.append(f);
+ return f;
+}
+
+FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
+{
+ BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code);
+ return f;
+}
+
+FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int))
+{
+ BuiltinFunction *f = new (memoryManager) BuiltinFunction(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();
+ object->prototype = objectPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newObjectCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) ObjectCtor(ctx);
+}
+
+String *ExecutionEngine::newString(const QString &s)
+{
+ return new (memoryManager) String(s);
+}
+
+Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value)
+{
+ StringObject *object = new (memoryManager) StringObject(ctx, value);
+ object->prototype = stringPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newStringCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) StringCtor(ctx);
+}
+
+Object *ExecutionEngine::newNumberObject(const Value &value)
+{
+ NumberObject *object = new (memoryManager) NumberObject(value);
+ object->prototype = numberPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newNumberCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) NumberCtor(ctx);
+}
+
+Object *ExecutionEngine::newBooleanObject(const Value &value)
+{
+ Object *object = new (memoryManager) BooleanObject(value);
+ object->prototype = booleanPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newBooleanCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) BooleanCtor(ctx);
+}
+
+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;
+}
+
+ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx, const Array &value)
+{
+ ArrayObject *object = new (memoryManager) ArrayObject(ctx, value);
+ object->prototype = arrayPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newArrayCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) ArrayCtor(ctx);
+}
+
+Object *ExecutionEngine::newDateObject(const Value &value)
+{
+ Object *object = new (memoryManager) DateObject(value);
+ object->prototype = datePrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newDateCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) DateCtor(ctx);
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
+{
+ bool global = (flags & IR::RegExp::RegExp_Global);
+ bool ignoreCase = false;
+ bool multiline = false;
+ if (flags & IR::RegExp::RegExp_IgnoreCase)
+ ignoreCase = true;
+ if (flags & IR::RegExp::RegExp_Multiline)
+ multiline = true;
+
+ return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global);
+}
+
+RegExpObject *ExecutionEngine::newRegExpObject(PassRefPtr<RegExp> re, bool global)
+{
+ RegExpObject *object = new (memoryManager) RegExpObject(this, re, global);
+ object->prototype = regExpPrototype;
+ return object;
+}
+
+FunctionObject *ExecutionEngine::newRegExpCtor(ExecutionContext *ctx)
+{
+ return new (memoryManager) RegExpCtor(ctx);
+}
+
+Object *ExecutionEngine::newErrorObject(const Value &value)
+{
+ ErrorObject *object = new (memoryManager) ErrorObject(this, value);
+ object->prototype = errorPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+{
+ return new (memoryManager) SyntaxErrorObject(ctx, message);
+}
+
+Object *ExecutionEngine::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::newMathObject(ExecutionContext *ctx)
+{
+ MathObject *object = new (memoryManager) MathObject(ctx);
+ object->prototype = objectPrototype;
+ return object;
+}
+
+Object *ExecutionEngine::newActivationObject()
+{
+ return new (memoryManager) Object();
+}
+
+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;
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set);
+ pd.configurable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ argumentsAccessors[i] = pd;
+ }
+}
+
+void ExecutionEngine::markObjects()
+{
+ globalObject.mark();
+
+ if (globalCode)
+ globalCode->mark();
+
+ exception.mark();
+
+ for (int i = 0; i < argumentsAccessors.size(); ++i) {
+ const PropertyDescriptor &pd = argumentsAccessors.at(i);
+ pd.get->mark();
+ pd.set->mark();
+ }
+
+
+ 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();
+}
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/v4/qmljs_engine.h b/src/v4/qmljs_engine.h
new file mode 100644
index 0000000000..f7416880a2
--- /dev/null
+++ b/src/v4/qmljs_engine.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 QMLJS_ENGINE_H
+#define QMLJS_ENGINE_H
+
+#include "qv4global.h"
+#include "qv4isel_p.h"
+#include "qv4object.h"
+#include "qmljs_environment.h"
+#include <setjmp.h>
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/BumpPointerAllocator.h>
+
+namespace QQmlJS {
+
+namespace Debugging {
+class Debugger;
+} // namespace Debugging
+
+namespace VM {
+
+struct Value;
+class Array;
+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;
+
+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;
+
+class RegExp;
+
+struct Q_V4_EXPORT ExecutionEngine
+{
+ MemoryManager *memoryManager;
+ EvalISelFactory *iselFactory;
+ ExecutionContext *current;
+ ExecutionContext *rootContext;
+ WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine.
+
+ Debugging::Debugger *debugger;
+
+ Value 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;
+
+ EvalFunction *evalFunction;
+
+ QVector<PropertyDescriptor> argumentsAccessors;
+
+ 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;
+
+ struct ExceptionHandler {
+ ExecutionContext *context;
+ const uchar *code; // Interpreter state
+ Value *target; // Interpreter state
+ jmp_buf stackFrame;
+ };
+
+ QVector<ExceptionHandler> unwindStack;
+ Value exception;
+
+ QVector<Function *> functions;
+
+ ExecutionEngine(EvalISelFactory *iselFactory);
+ ~ExecutionEngine();
+
+ ExecutionContext *newContext();
+
+ String *identifier(const QString &s);
+
+ VM::Function *newFunction(const QString &name);
+
+ FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *));
+ FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int));
+ FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function);
+ BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
+
+ Object *newObject();
+ FunctionObject *newObjectCtor(ExecutionContext *ctx);
+
+ String *newString(const QString &s);
+ Object *newStringObject(ExecutionContext *ctx, const Value &value);
+ FunctionObject *newStringCtor(ExecutionContext *ctx);
+
+ Object *newNumberObject(const Value &value);
+ FunctionObject *newNumberCtor(ExecutionContext *ctx);
+
+ Object *newBooleanObject(const Value &value);
+ FunctionObject *newBooleanCtor(ExecutionContext *ctx);
+
+ Object *newFunctionObject(ExecutionContext *ctx);
+
+ ArrayObject *newArrayObject(ExecutionContext *ctx);
+ ArrayObject *newArrayObject(ExecutionContext *ctx, const Array &value);
+ FunctionObject *newArrayCtor(ExecutionContext *ctx);
+
+ Object *newDateObject(const Value &value);
+ FunctionObject *newDateCtor(ExecutionContext *ctx);
+
+ RegExpObject *newRegExpObject(const QString &pattern, int flags);
+ RegExpObject *newRegExpObject(PassRefPtr<RegExp> re, bool global);
+ FunctionObject *newRegExpCtor(ExecutionContext *ctx);
+
+ 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 *newMathObject(ExecutionContext *ctx);
+ Object *newActivationObject();
+
+ Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o);
+
+ void requireArgumentsAccessors(int n);
+
+ void markObjects();
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif
diff --git a/src/v4/qmljs_environment.cpp b/src/v4/qmljs_environment.cpp
new file mode 100644
index 0000000000..9042a435a0
--- /dev/null
+++ b/src/v4/qmljs_environment.cpp
@@ -0,0 +1,564 @@
+/****************************************************************************
+**
+** 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 <qmljs_environment.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);
+}
+
+bool ExecutionContext::hasBinding(String *name) const
+{
+ if (!function)
+ return false;
+
+ for (unsigned int i = 0; i < function->varCount; ++i) {
+ if (function->varList[i]->isEqualTo(name))
+ return true;
+ }
+ for (unsigned int i = 0; i < function->formalParameterCount; ++i) {
+ if (function->formalParameterList[i]->isEqualTo(name))
+ return true;
+ }
+ if (activation)
+ return activation->__hasProperty__(this, name);
+ return false;
+}
+
+void ExecutionContext::createMutableBinding(String *name, bool deletable)
+{
+ if (!activation)
+ activation = engine->newActivationObject();
+
+ if (activation->__hasProperty__(this, name))
+ return;
+ PropertyDescriptor desc;
+ desc.value = Value::undefinedValue();
+ desc.type = PropertyDescriptor::Data;
+ desc.configurable = deletable ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled;
+ desc.writable = PropertyDescriptor::Enabled;
+ desc.enumberable = PropertyDescriptor::Enabled;
+ activation->__defineOwnProperty__(this, name, &desc);
+}
+
+bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value)
+{
+ // ### throw if scope->strict is true, and it would change an immutable binding
+ if (function) {
+ for (unsigned int i = 0; i < function->varCount; ++i)
+ if (function->varList[i]->isEqualTo(name)) {
+ locals[i] = value;
+ return true;
+ }
+ for (int i = (int)function->formalParameterCount - 1; i >= 0; --i)
+ if (function->formalParameterList[i]->isEqualTo(name)) {
+ arguments[i] = value;
+ return true;
+ }
+ }
+
+ if (activation && activation->__hasProperty__(scope, name)) {
+ activation->__put__(scope, name, value);
+ return true;
+ }
+
+ return false;
+}
+
+Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const
+{
+ Q_UNUSED(strict);
+ assert(function);
+
+ if (function) {
+ for (unsigned int i = 0; i < function->varCount; ++i)
+ if (function->varList[i]->isEqualTo(name))
+ return locals[i];
+ for (int i = (int)function->formalParameterCount - 1; i >= 0; --i)
+ if (function->formalParameterList[i]->isEqualTo(name))
+ return arguments[i];
+ }
+
+ if (activation) {
+ bool hasProperty = false;
+ Value v = activation->__get__(scope, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ assert(false);
+}
+
+bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name)
+{
+ if (activation)
+ activation->__delete__(scope, name);
+
+ if (scope->strictMode)
+ __qmljs_throw_type_error(scope);
+ return false;
+}
+
+ExecutionContext *ExecutionContext::createWithScope(Object *with)
+{
+ ExecutionContext *withCtx = engine->newContext();
+ withCtx->init(this, with);
+ engine->current = withCtx;
+ return withCtx;
+}
+
+ExecutionContext *ExecutionContext::popScope()
+{
+ assert(engine->current == this);
+ assert(withObject != 0);
+
+ engine->current = parent;
+ parent = 0;
+ return engine->current;
+}
+
+String * const *ExecutionContext::formals() const
+{
+ return function ? function->formalParameterList : 0;
+}
+
+unsigned int ExecutionContext::formalCount() const
+{
+ return function ? function->formalParameterCount : 0;
+}
+
+String * const *ExecutionContext::variables() const
+{
+ return function ? function->varList : 0;
+}
+
+unsigned int ExecutionContext::variableCount() const
+{
+ return function ? function->varCount : 0;
+}
+
+
+void ExecutionContext::init(ExecutionEngine *eng)
+{
+ engine = eng;
+ parent = 0;
+ outer = 0;
+ thisObject = eng->globalObject;
+
+ function = 0;
+ arguments = 0;
+ argumentCount = 0;
+ locals = 0;
+ strictMode = false;
+ activation = 0;
+ withObject = 0;
+
+ eng->exception = Value::undefinedValue();
+}
+
+void ExecutionContext::init(ExecutionContext *p, Object *with)
+{
+ engine = p->engine;
+ parent = p;
+ outer = p;
+ thisObject = p->thisObject;
+
+ function = 0;
+ arguments = 0;
+ argumentCount = 0;
+ locals = 0;
+ strictMode = false;
+ activation = 0;
+ withObject = with;
+}
+
+void ExecutionContext::destroy()
+{
+ delete[] arguments;
+ delete[] locals;
+}
+
+bool ExecutionContext::deleteProperty(String *name)
+{
+ bool hasWith = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (ctx->withObject) {
+ hasWith = true;
+ if (ctx->withObject->__hasProperty__(this, name))
+ return ctx->withObject->__delete__(this, name);
+ } else {
+ if (ctx->activation && ctx->activation->__hasProperty__(this, name))
+ return ctx->activation->__delete__(this, name);
+ }
+ if (FunctionObject *f = ctx->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 (strictMode)
+ throwSyntaxError(0);
+ return true;
+}
+
+void ExecutionContext::mark()
+{
+ thisObject.mark();
+ if (function)
+ function->mark();
+ for (unsigned arg = 0, lastArg = formalCount(); arg < lastArg; ++arg)
+ arguments[arg].mark();
+ for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local)
+ locals[local].mark();
+ if (activation)
+ activation->mark();
+ if (withObject)
+ withObject->mark();
+}
+
+void ExecutionContext::setProperty(String *name, Value value)
+{
+// qDebug() << "=== SetProperty" << value.toString(this)->toQString();
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (Object *w = ctx->withObject) {
+// qDebug() << ctx << "hasWith";
+ if (w->__hasProperty__(ctx, name)) {
+// qDebug() << " withHasProp";
+ w->__put__(ctx, name, value);
+ return;
+ }
+ } else {
+// qDebug() << ctx << "setting mutable binding";
+ if (ctx->setMutableBinding(this, name, value))
+ return;
+ }
+ }
+ if (strictMode || name->isEqualTo(engine->id_this))
+ throwReferenceError(Value::fromString(name));
+ engine->globalObject.objectValue()->__put__(this, name, value);
+}
+
+Value ExecutionContext::getProperty(String *name)
+{
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+// qDebug() << "=== getProperty" << name->toQString();
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (Object *w = ctx->withObject) {
+ hasWith = true;
+// qDebug() << ctx << "hasWith";
+ bool hasProperty = false;
+ Value v = w->__get__(ctx, name, &hasProperty);
+ if (hasProperty) {
+// qDebug() << " withHasProp";
+ return v;
+ }
+ continue;
+ }
+
+ if (FunctionObject *f = ctx->function) {
+ if (f->needsActivation || hasWith) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return ctx->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return ctx->arguments[i];
+ }
+ }
+ if (ctx->activation) {
+ bool hasProperty = false;
+ Value v = ctx->activation->__get__(ctx, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ throwReferenceError(Value::fromString(name));
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyNoThrow(String *name)
+{
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (Object *w = ctx->withObject) {
+ hasWith = true;
+ bool hasProperty = false;
+ Value v = w->__get__(ctx, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ continue;
+ }
+
+ if (FunctionObject *f = ctx->function) {
+ if (f->needsActivation || hasWith) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return ctx->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return ctx->arguments[i];
+ }
+ }
+ if (ctx->activation) {
+ bool hasProperty = false;
+ Value v = ctx->activation->__get__(ctx, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ return Value::undefinedValue();
+}
+
+Value ExecutionContext::getPropertyAndBase(String *name, Object **base)
+{
+ *base = 0;
+
+ if (name->isEqualTo(engine->id_this))
+ return thisObject;
+
+ bool hasWith = false;
+ for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+ if (Object *w = ctx->withObject) {
+ hasWith = true;
+ bool hasProperty = false;
+ Value v = w->__get__(ctx, name, &hasProperty);
+ if (hasProperty) {
+ *base = w;
+ return v;
+ }
+ continue;
+ }
+
+ if (FunctionObject *f = ctx->function) {
+ if (f->needsActivation || hasWith) {
+ for (unsigned int i = 0; i < f->varCount; ++i)
+ if (f->varList[i]->isEqualTo(name))
+ return ctx->locals[i];
+ for (int i = (int)f->formalParameterCount - 1; i >= 0; --i)
+ if (f->formalParameterList[i]->isEqualTo(name))
+ return ctx->arguments[i];
+ }
+ }
+ if (ctx->activation) {
+ bool hasProperty = false;
+ Value v = ctx->activation->__get__(ctx, name, &hasProperty);
+ if (hasProperty)
+ return v;
+ }
+ }
+ throwReferenceError(Value::fromString(name));
+ return Value::undefinedValue();
+}
+
+
+
+void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op)
+{
+ Value lhs = getProperty(name);
+ value = op(lhs, value, this);
+ setProperty(name, value);
+}
+
+void ExecutionContext::throwError(Value value)
+{
+ __qmljs_builtin_throw(value, this);
+}
+
+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)));
+}
+
+void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc)
+{
+ MemoryManager::GCBlocker blockGC(parent->engine->memoryManager);
+
+ engine = parent->engine;
+ this->parent = parent;
+ outer = f->scope;
+ engine->current = this;
+
+ function = f;
+ strictMode = f->strictMode;
+
+ thisObject = that;
+ if (!strictMode && !thisObject.isObject()) {
+ if (thisObject.isUndefined() || thisObject.isNull())
+ thisObject = engine->globalObject;
+ else
+ thisObject = thisObject.toObject(this);
+ }
+
+ locals = function->varCount ? reinterpret_cast<Value *>(this + 1) : 0;
+ if (locals)
+ std::fill(locals, locals + function->varCount, Value::undefinedValue());
+
+ arguments = args;
+ argumentCount = argc;
+ if (function->needsActivation || argc < function->formalParameterCount){
+ argumentCount = qMax(argc, function->formalParameterCount);
+ arguments = reinterpret_cast<Value *>(this + 1) + function->varCount;
+ if (argc)
+ std::copy(args, args + argc, arguments);
+ if (argc < function->formalParameterCount)
+ std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
+ }
+
+
+ activation = 0;
+ withObject = 0;
+
+ if (function->usesArgumentsObject) {
+ ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc);
+ args->prototype = engine->objectPrototype;
+ Value arguments = Value::fromObject(args);
+ createMutableBinding(engine->id_arguments, false);
+ setMutableBinding(this, engine->id_arguments, arguments);
+ }
+
+ if (engine->debugger)
+ engine->debugger->aboutToCall(f, this);
+}
+
+void ExecutionContext::leaveCallContext()
+{
+ if (!function->needsActivation)
+ locals = 0;
+ engine->current = parent;
+ parent = 0;
+
+ if (engine->debugger)
+ engine->debugger->justLeft(this);
+}
+
+void ExecutionContext::wireUpPrototype()
+{
+ assert(thisObject.isObject());
+
+ Value proto = function->__get__(this, engine->id_prototype);
+ if (proto.isObject())
+ thisObject.objectValue()->prototype = proto.objectValue();
+ else
+ thisObject.objectValue()->prototype = engine->objectPrototype;
+}
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/v4/qmljs_environment.h b/src/v4/qmljs_environment.h
new file mode 100644
index 0000000000..111c6ae239
--- /dev/null
+++ b/src/v4/qmljs_environment.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** 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 <qmljs_runtime.h>
+
+namespace QQmlJS {
+namespace VM {
+
+struct Value;
+struct Object;
+struct ExecutionEngine;
+struct ExecutionContext;
+struct DeclarativeEnvironment;
+
+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 ExecutionContext
+{
+ ExecutionEngine *engine;
+ ExecutionContext *parent;
+ ExecutionContext *outer;
+ Value thisObject;
+
+ FunctionObject *function;
+
+ Value *arguments;
+ unsigned int argumentCount;
+ Value *locals;
+
+ String * const *formals() const;
+ unsigned int formalCount() const;
+ String * const *variables() const;
+ unsigned int variableCount() const;
+
+ bool strictMode;
+
+ Object *activation;
+ Object *withObject;
+
+ void init(ExecutionEngine *e);
+ void init(ExecutionContext *p, Object *with);
+ void destroy();
+
+ bool hasBinding(String *name) const;
+ void createMutableBinding(String *name, bool deletable);
+ bool setMutableBinding(ExecutionContext *scope, String *name, Value value);
+ Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const;
+ bool deleteBinding(ExecutionContext *ctx, String *name);
+
+ ExecutionContext *createWithScope(Object *with);
+ ExecutionContext *popScope();
+
+ void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc);
+ void leaveCallContext();
+
+ void wireUpPrototype();
+
+ void throwError(Value value);
+ void throwError(const QString &message);
+ void throwSyntaxError(DiagnosticMessage *message);
+ void throwTypeError();
+ void throwReferenceError(Value value);
+ void throwRangeError(Value value);
+ void throwURIError(Value msg);
+ void throwUnimplemented(const QString &message);
+
+ void setProperty(String *name, Value value);
+ Value getProperty(String *name);
+ Value getPropertyNoThrow(String *name);
+ Value getPropertyAndBase(String *name, Object **base);
+ void inplaceBitOp(Value value, String *name, BinOp op);
+ bool deleteProperty(String *name);
+
+ inline Value argument(unsigned int index = 0)
+ {
+ if (index < argumentCount)
+ return arguments[index];
+ return Value::undefinedValue();
+ }
+
+ void mark();
+
+};
+
+/* Function *f, int argc */
+#define requiredMemoryForExecutionContect(f, argc) \
+ sizeof(ExecutionContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount))
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif
diff --git a/src/v4/qmljs_math.h b/src/v4/qmljs_math.h
new file mode 100644
index 0000000000..4645c5a324
--- /dev/null
+++ b/src/v4/qmljs_math.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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) && 1 //CPU(X86_64)
+
+namespace QQmlJS {
+namespace VM {
+
+static inline 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 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 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
+
+#endif // !defined(QMLJS_LLVM_RUNTIME) && 1 //CPU(X86_64)
+#endif // QMLJS_MATH_H
diff --git a/src/v4/qmljs_runtime.cpp b/src/v4/qmljs_runtime.cpp
new file mode 100644
index 0000000000..ce1d1abbaa
--- /dev/null
+++ b/src/v4/qmljs_runtime.cpp
@@ -0,0 +1,1161 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qv4object.h"
+#include "qv4ir_p.h"
+#include "qv4objectproto.h"
+#include "qv4globalobject.h"
+#include "qv4stringobject.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"
+
+namespace QQmlJS {
+namespace VM {
+
+QString numberToString(double num, int radix = 10)
+{
+ if (std::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;
+}
+
+extern "C" {
+
+Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx)
+{
+ assert(clos);
+ return 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;
+}
+
+Value __qmljs_string_literal_undefined(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("undefined")));
+}
+
+Value __qmljs_string_literal_null(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("null")));
+}
+
+Value __qmljs_string_literal_true(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("true")));
+}
+
+Value __qmljs_string_literal_false(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("false")));
+}
+
+Value __qmljs_string_literal_object(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("object")));
+}
+
+Value __qmljs_string_literal_boolean(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("boolean")));
+}
+
+Value __qmljs_string_literal_number(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("number")));
+}
+
+Value __qmljs_string_literal_string(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("string")));
+}
+
+Value __qmljs_string_literal_function(ExecutionContext *ctx)
+{
+ return Value::fromString(ctx->engine->identifier(QStringLiteral("function")));
+}
+
+Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index)
+{
+ if (Object *o = base.asObject()) {
+ uint n = UINT_MAX;
+ if (index.isInteger())
+ n = index.integerValue();
+ else if (index.isDouble())
+ n = index.doubleValue();
+ if (n < UINT_MAX)
+ return Value::fromBoolean(o->__delete__(ctx, n));
+ }
+
+ String *name = index.toString(ctx);
+ return __qmljs_delete_member(ctx, base, name);
+}
+
+Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name)
+{
+ Value obj = base.toObject(ctx);
+ return Value::fromBoolean(obj.objectValue()->__delete__(ctx, name));
+}
+
+Value __qmljs_delete_name(ExecutionContext *ctx, String *name)
+{
+ return Value::fromBoolean(ctx->deleteProperty(name));
+}
+
+Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx)
+{
+ Value pleft = __qmljs_to_primitive(left, ctx, PREFERREDTYPE_HINT);
+ Value pright = __qmljs_to_primitive(right, ctx, 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());
+ return Value::fromString(string);
+ }
+ double x = __qmljs_to_number(pleft, ctx);
+ double y = __qmljs_to_number(pright, ctx);
+ return Value::fromDouble(x + y);
+}
+
+Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx)
+{
+ if (FunctionObject *function = right.asFunctionObject()) {
+ bool r = function->hasInstance(ctx, left);
+ return Value::fromBoolean(r);
+ }
+
+ return __qmljs_throw_type_error(ctx);
+}
+
+Value __qmljs_in(Value left, Value right, ExecutionContext *ctx)
+{
+ if (right.isObject()) {
+ String *s = left.toString(ctx);
+ bool r = right.objectValue()->__hasProperty__(ctx, s);
+ return Value::fromBoolean(r);
+ } else {
+ return __qmljs_throw_type_error(ctx);
+ }
+}
+
+void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_bit_and);
+}
+
+void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_bit_or);
+}
+
+void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_bit_xor);
+}
+
+void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_add);
+}
+
+void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_sub);
+}
+
+void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_mul);
+}
+
+void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_div);
+}
+
+void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_mod);
+}
+
+void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_shl);
+}
+
+void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_shr);
+}
+
+void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx)
+{
+ ctx->inplaceBitOp(value, name, __qmljs_ushr);
+}
+
+void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_bit_and, ctx);
+}
+
+void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_bit_or, ctx);
+}
+
+void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_bit_xor, ctx);
+}
+
+void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_add, ctx);
+}
+
+void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_sub, ctx);
+}
+
+void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_mul, ctx);
+}
+
+void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_div, ctx);
+}
+
+void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_mod, ctx);
+}
+
+void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_shl, ctx);
+}
+
+void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_shr, ctx);
+}
+
+void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx)
+{
+ Object *obj = base.toObject(ctx).objectValue();
+ obj->inplaceBinOp(value, index, __qmljs_ushr, ctx);
+}
+
+void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_bit_and, ctx);
+}
+
+void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_bit_or, ctx);
+}
+
+void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_bit_xor, ctx);
+}
+
+void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_add, ctx);
+}
+
+void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_sub, ctx);
+}
+
+void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_mul, ctx);
+}
+
+void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_div, ctx);
+}
+
+void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_mod, ctx);
+}
+
+void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_shl, ctx);
+}
+
+void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_shr, ctx);
+}
+
+void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx)
+{
+ Object *o = base.toObject(ctx).objectValue();
+ o->inplaceBinOp(value, name, __qmljs_ushr, ctx);
+}
+
+String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s)
+{
+ return ctx->engine->newString(QString::fromUtf8(s));
+}
+
+String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s)
+{
+ return ctx->engine->identifier(QString::fromUtf8(s));
+}
+
+int __qmljs_string_length(ExecutionContext *, String *string)
+{
+ return string->toQString().length();
+}
+
+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 = INFINITY;
+ else if (ba == "-Infinity")
+ d = -INFINITY;
+ else
+ d = nan("");
+ }
+ return d;
+}
+
+Value __qmljs_string_from_number(ExecutionContext *ctx, double number)
+{
+ String *string = ctx->engine->newString(numberToString(number, 10));
+ return Value::fromString(string);
+}
+
+Bool __qmljs_string_compare(ExecutionContext *, String *left, String *right)
+{
+ return left->toQString() < right->toQString();
+}
+
+Bool __qmljs_string_equal(String *left, String *right)
+{
+ return left->isEqualTo(right);
+}
+
+String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second)
+{
+ return ctx->engine->newString(first->toQString() + second->toQString());
+}
+
+Bool __qmljs_is_function(Value value)
+{
+ return value.objectValue()->asFunctionObject() != 0;
+}
+
+Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint)
+{
+ if (typeHint == PREFERREDTYPE_HINT) {
+ if (object.asDateObject())
+ typeHint = STRING_HINT;
+ else
+ typeHint = NUMBER_HINT;
+ }
+
+ String *meth1 = ctx->engine->identifier("toString");
+ String *meth2 = ctx->engine->identifier("valueOf");
+
+ if (typeHint == NUMBER_HINT)
+ qSwap(meth1, meth2);
+
+ assert(object.isObject());
+ Object *oo = object.objectValue();
+
+ Value conv = oo->__get__(ctx, meth1);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(ctx, object, 0, 0);
+ if (r.isPrimitive())
+ return r;
+ }
+
+ conv = oo->__get__(ctx, meth2);
+ if (FunctionObject *o = conv.asFunctionObject()) {
+ Value r = o->call(ctx, object, 0, 0);
+ if (r.isPrimitive())
+ return r;
+ }
+
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Value __qmljs_throw_type_error(ExecutionContext *ctx)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Value __qmljs_new_object(ExecutionContext *ctx)
+{
+ return Value::fromObject(ctx->engine->newObject());
+}
+
+Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean)
+{
+ Value value = Value::fromBoolean(boolean);
+ return Value::fromObject(ctx->engine->newBooleanObject(value));
+}
+
+Value __qmljs_new_number_object(ExecutionContext *ctx, double number)
+{
+ Value value = Value::fromDouble(number);
+ return Value::fromObject(ctx->engine->newNumberObject(value));
+}
+
+Value __qmljs_new_string_object(ExecutionContext *ctx, String *string)
+{
+ Value value = Value::fromString(string);
+ return Value::fromObject(ctx->engine->newStringObject(ctx, value));
+}
+
+void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value)
+{
+ if (! object.isObject())
+ object = __qmljs_to_object(object, ctx);
+ object.objectValue()->__put__(ctx, name, value);
+}
+
+Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index)
+{
+ uint type = object.type();
+ uint idx = index.asArrayIndex();
+
+ if (type != Value::Object_Type) {
+ if (type == Value::String_Type && idx < UINT_MAX) {
+ String *str = object.stringValue();
+ if (idx >= (uint)str->toQString().length())
+ return Value::undefinedValue();
+ const QString s = str->toQString().mid(idx, 1);
+ return Value::fromString(ctx, s);
+ }
+
+ object = __qmljs_to_object(object, ctx);
+ }
+
+ Object *o = object.objectValue();
+
+ if (idx < UINT_MAX) {
+ const PropertyDescriptor *p = o->array.nonSparseAt(idx);
+ if (p && p->type == PropertyDescriptor::Data)
+ return p->value;
+
+ return o->__get__(ctx, idx);
+ }
+
+ String *name = index.toString(ctx);
+ return o->__get__(ctx, name);
+}
+
+void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value)
+{
+ if (!object.isObject())
+ object = __qmljs_to_object(object, ctx);
+
+ Object *o = object.objectValue();
+
+ uint idx = index.asArrayIndex();
+ if (idx < UINT_MAX) {
+ PropertyDescriptor *p = o->array.nonSparseAtRef(idx);
+ if (p && p->type == PropertyDescriptor::Data && p->isWritable()) {
+ p->value = value;
+ return;
+ }
+ o->__put__(ctx, idx, value);
+ return;
+ }
+
+ String *name = index.toString(ctx);
+ o->__put__(ctx, name, value);
+}
+
+Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx)
+{
+ if (!in.isNull() && !in.isUndefined())
+ in = __qmljs_to_object(in, ctx);
+ Object *it = ctx->engine->newForEachIteratorObject(ctx, in.asObject());
+ return Value::fromObject(it);
+}
+
+Value __qmljs_foreach_next_property_name(Value foreach_iterator)
+{
+ assert(foreach_iterator.isObject());
+
+ ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue());
+ assert(it->asForeachIteratorObject());
+
+ return it->nextPropertyName();
+}
+
+
+void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value)
+{
+ ctx->setProperty(name, value);
+}
+
+Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name)
+{
+ if (object.isObject()) {
+ return object.objectValue()->__get__(ctx, name);
+ } else if (object.isString() && name->isEqualTo(ctx->engine->id_length)) {
+ return Value::fromInt32(object.stringValue()->toQString().length());
+ } else {
+ object = __qmljs_to_object(object, ctx);
+
+ if (object.isObject()) {
+ return object.objectValue()->__get__(ctx, name);
+ } else {
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+ }
+ }
+}
+
+Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name)
+{
+ return ctx->getProperty(name);
+}
+
+Value __qmljs_get_thisObject(ExecutionContext *ctx)
+{
+ return ctx->thisObject;
+}
+
+uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx)
+{
+ 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:
+ 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, ctx));
+ return __qmljs_equal(x, ny, ctx);
+ } else if (x.isString() && y.isNumber()) {
+ Value nx = Value::fromDouble(__qmljs_to_number(x, ctx));
+ return __qmljs_equal(nx, y, ctx);
+ } else if (x.isBoolean()) {
+ Value nx = Value::fromDouble((double) x.booleanValue());
+ return __qmljs_equal(nx, y, ctx);
+ } else if (y.isBoolean()) {
+ Value ny = Value::fromDouble((double) y.booleanValue());
+ return __qmljs_equal(x, ny, ctx);
+ } else if ((x.isNumber() || x.isString()) && y.isObject()) {
+ Value py = __qmljs_to_primitive(y, ctx, PREFERREDTYPE_HINT);
+ return __qmljs_equal(x, py, ctx);
+ } else if (x.isObject() && (y.isNumber() || y.isString())) {
+ Value px = __qmljs_to_primitive(x, ctx, PREFERREDTYPE_HINT);
+ return __qmljs_equal(px, y, ctx);
+ }
+ }
+
+ return false;
+}
+
+Value __qmljs_call_activation_property(ExecutionContext *context, 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))
+ return static_cast<EvalFunction *>(o)->evalCall(context, thisObject, args, argc, true);
+
+ return o->call(context, thisObject, args, argc);
+}
+
+Value __qmljs_call_property(ExecutionContext *context, Value thisObject, String *name, Value *args, int argc)
+{
+ Object *baseObject;
+ if (thisObject.isString()) {
+ baseObject = context->engine->stringPrototype;
+ } else {
+ if (!thisObject.isObject())
+ thisObject = __qmljs_to_object(thisObject, context);
+
+ assert(thisObject.isObject());
+ baseObject = thisObject.objectValue();
+ }
+
+ Value func = baseObject->__get__(context, name);
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ return o->call(context, thisObject, args, argc);
+}
+
+Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc)
+{
+ Value thisObject = that;
+ if (!thisObject.isObject())
+ thisObject = __qmljs_to_object(thisObject, context);
+
+ assert(thisObject.isObject());
+ Object *baseObject = thisObject.objectValue();
+
+ Value func = baseObject->__get__(context, index.toString(context));
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+
+ return o->call(context, thisObject, args, argc);
+}
+
+Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc)
+{
+ FunctionObject *o = func.asFunctionObject();
+ if (!o)
+ context->throwTypeError();
+ return o->call(context, thisObject, args, argc);
+}
+
+Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc)
+{
+ Value func = context->getProperty(name);
+ return __qmljs_construct_value(context, func, args, argc);
+}
+
+Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc)
+{
+ if (FunctionObject *f = func.asFunctionObject())
+ return f->construct(context, args, argc);
+
+ context->throwTypeError();
+ return Value::undefinedValue();
+}
+
+Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc)
+{
+ Value thisObject = base;
+ if (!thisObject.isObject())
+ thisObject = __qmljs_to_object(base, context);
+
+ Value func = thisObject.objectValue()->__get__(context, name);
+ if (FunctionObject *f = func.asFunctionObject())
+ return f->construct(context, args, argc);
+
+ context->throwTypeError();
+ return Value::undefinedValue();
+}
+
+void __qmljs_throw(Value value, ExecutionContext *context)
+{
+ assert(!context->engine->unwindStack.isEmpty());
+
+ if (context->engine->debugger)
+ context->engine->debugger->aboutToThrow(&value);
+
+ ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
+
+ // clean up call contexts
+ while (context != handler.context) {
+ ExecutionContext *parent = context->parent;
+ if (!context->withObject)
+ context->leaveCallContext();
+ context = parent;
+ }
+
+ context->engine->exception = value;
+
+ longjmp(handler.stackFrame, 1);
+}
+
+Q_V4_EXPORT void * __qmljs_create_exception_handler(ExecutionContext *context)
+{
+ context->engine->exception = Value::undefinedValue();
+ context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
+ ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
+ handler.context = context;
+ return handler.stackFrame;
+}
+
+void __qmljs_delete_exception_handler(ExecutionContext *context)
+{
+ assert(!context->engine->unwindStack.isEmpty());
+
+ context->engine->unwindStack.pop_back();
+}
+
+Value __qmljs_get_exception(ExecutionContext *context)
+{
+ return context->engine->exception;
+}
+
+Value __qmljs_builtin_typeof(Value value, ExecutionContext *ctx)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ return __qmljs_string_literal_undefined(ctx);
+ break;
+ case Value::Null_Type:
+ return __qmljs_string_literal_object(ctx);
+ break;
+ case Value::Boolean_Type:
+ return __qmljs_string_literal_boolean(ctx);
+ break;
+ case Value::String_Type:
+ return __qmljs_string_literal_string(ctx);
+ break;
+ case Value::Object_Type:
+ if (__qmljs_is_callable(value, ctx))
+ return __qmljs_string_literal_function(ctx);
+ else
+ return __qmljs_string_literal_object(ctx); // ### implementation-defined
+ break;
+ default:
+ return __qmljs_string_literal_number(ctx);
+ break;
+ }
+}
+
+Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context)
+{
+ return __qmljs_builtin_typeof(context->getPropertyNoThrow(name), context);
+}
+
+Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context)
+{
+ Value obj = base.toObject(context);
+ return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context);
+}
+
+Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context)
+{
+ String *name = index.toString(context);
+ Value obj = base.toObject(context);
+ return __qmljs_builtin_typeof(obj.objectValue()->__get__(context, name), context);
+}
+
+Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx)
+{
+ if (val->isInteger() && val->integerValue() < INT_MAX) {
+ Value retval = *val;
+ val->int_32 += 1;
+ return retval;
+ }
+
+ double d = __qmljs_to_number(*val, ctx);
+ *val = Value::fromDouble(d + 1);
+ return Value::fromDouble(d);
+}
+
+Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context)
+{
+ Value v = context->getProperty(name);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ retval = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ context->setProperty(name, v);
+ return retval;
+}
+
+Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context)
+{
+ Object *o = __qmljs_to_object(base, context).objectValue();
+
+ Value v = o->__get__(context, name);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ retval = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ o->__put__(context, name, v);
+ return retval;
+}
+
+Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context)
+{
+ Object *o = __qmljs_to_object(base, context).objectValue();
+
+ uint idx = index.asArrayIndex();
+
+ if (idx == UINT_MAX) {
+ String *s = index.toString(context);
+ return __qmljs_builtin_post_increment_member(base, s, context);
+ }
+
+ Value v = o->__get__(context, idx);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() < INT_MAX) {
+ retval = v;
+ v.int_32 += 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d + 1);
+ }
+
+ o->__put__(context, idx, v);
+ return retval;
+}
+
+Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx)
+{
+ if (val->isInteger() && val->integerValue() > INT_MIN) {
+ Value retval = *val;
+ val->int_32 -= 1;
+ return retval;
+ }
+
+ double d = __qmljs_to_number(*val, ctx);
+ *val = Value::fromDouble(d - 1);
+ return Value::fromDouble(d);
+}
+
+Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context)
+{
+ Value v = context->getProperty(name);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ retval = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ context->setProperty(name, v);
+ return retval;
+}
+
+Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context)
+{
+ Object *o = __qmljs_to_object(base, context).objectValue();
+
+ Value v = o->__get__(context, name);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ retval = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ o->__put__(context, name, v);
+ return retval;
+}
+
+Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context)
+{
+ Object *o = __qmljs_to_object(base, context).objectValue();
+
+ uint idx = index.asArrayIndex();
+
+ if (idx == UINT_MAX) {
+ String *s = index.toString(context);
+ return __qmljs_builtin_post_decrement_member(base, s, context);
+ }
+
+ Value v = o->__get__(context, idx);
+ Value retval;
+
+ if (v.isInteger() && v.integerValue() > INT_MIN) {
+ retval = v;
+ v.int_32 -= 1;
+ } else {
+ double d = __qmljs_to_number(v, context);
+ retval = Value::fromDouble(d);
+ v = Value::fromDouble(d - 1);
+ }
+
+ o->__put__(context, idx, v);
+ return retval;
+}
+
+void __qmljs_builtin_throw(Value val, ExecutionContext *context)
+{
+ __qmljs_throw(val, context);
+}
+
+ExecutionContext *__qmljs_builtin_push_with_scope(Value o, ExecutionContext *ctx)
+{
+ Object *obj = __qmljs_to_object(o, ctx).asObject();
+ return ctx->createWithScope(obj);
+}
+
+ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx)
+{
+ return ctx->popScope();
+}
+
+void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name)
+{
+ ctx->createMutableBinding(name, deletable);
+}
+
+void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx)
+{
+ Object *o = object.asObject();
+ assert(o);
+
+ PropertyDescriptor pd;
+ pd.value = val;
+ pd.type = PropertyDescriptor::Data;
+ pd.writable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ pd.configurable = PropertyDescriptor::Enabled;
+ o->__defineOwnProperty__(ctx, name, &pd);
+}
+
+void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx)
+{
+ Object *o = object.asObject();
+ assert(o);
+
+ PropertyDescriptor pd;
+ pd.value = val;
+ pd.type = PropertyDescriptor::Data;
+ pd.writable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ pd.configurable = PropertyDescriptor::Enabled;
+ o->__defineOwnProperty__(ctx, index, &pd);
+}
+
+void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx)
+{
+ Object *o = object.asObject();
+ assert(o);
+
+ PropertyDescriptor pd;
+ pd.get = getter.asFunctionObject();
+ pd.set = setter.asFunctionObject();
+ pd.type = PropertyDescriptor::Accessor;
+ pd.writable = PropertyDescriptor::Undefined;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ pd.configurable = PropertyDescriptor::Enabled;
+ o->__defineOwnProperty__(ctx, name, &pd);
+}
+
+Value __qmljs_increment(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ if (value.isInteger())
+ return Value::fromInt32(value.integerValue() + 1);
+
+ double d = __qmljs_to_number(value, ctx);
+ return Value::fromDouble(d + 1);
+}
+
+Value __qmljs_decrement(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ if (value.isInteger())
+ return Value::fromInt32(value.integerValue() - 1);
+
+ double d = __qmljs_to_number(value, ctx);
+ return Value::fromDouble(d - 1);
+}
+
+} // extern "C"
+
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/v4/qmljs_runtime.h b/src/v4/qmljs_runtime.h
new file mode 100644
index 0000000000..2ea612642c
--- /dev/null
+++ b/src/v4/qmljs_runtime.h
@@ -0,0 +1,862 @@
+/****************************************************************************
+**
+** 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 "qmljs_value.h"
+#include "qmljs_math.h"
+
+
+#include <QtCore/QString>
+#include <QtCore/qnumeric.h>
+#include <QtCore/QDebug>
+
+#include <cmath>
+#include <cassert>
+
+#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
+
+namespace QQmlJS {
+namespace VM {
+
+enum TypeHint {
+ PREFERREDTYPE_HINT,
+ NUMBER_HINT,
+ STRING_HINT
+};
+
+struct Function;
+struct Object;
+struct String;
+struct PropertyDescriptor;
+struct ExecutionContext;
+struct FunctionObject;
+struct BooleanObject;
+struct NumberObject;
+struct StringObject;
+struct DateObject;
+struct RegExpObject;
+struct ArrayObject;
+struct ErrorObject;
+struct ExecutionEngine;
+
+extern "C" {
+
+// context
+Value __qmljs_call_activation_property(ExecutionContext *, String *name, Value *args, int argc);
+Value __qmljs_call_property(ExecutionContext *context, Value that, String *name, Value *args, int argc);
+Value __qmljs_call_element(ExecutionContext *context, Value that, Value index, Value *args, int argc);
+Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func, Value *args, int argc);
+
+Value __qmljs_construct_activation_property(ExecutionContext *, String *name, Value *args, int argc);
+Value __qmljs_construct_property(ExecutionContext *context, Value base, String *name, Value *args, int argc);
+Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc);
+
+Value __qmljs_builtin_typeof(Value val, ExecutionContext *ctx);
+Value __qmljs_builtin_typeof_name(String *name, ExecutionContext *context);
+Value __qmljs_builtin_typeof_member(Value base, String *name, ExecutionContext *context);
+Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *context);
+
+Value __qmljs_builtin_post_increment(Value *val, ExecutionContext *ctx);
+Value __qmljs_builtin_post_increment_name(String *name, ExecutionContext *context);
+Value __qmljs_builtin_post_increment_member(Value base, String *name, ExecutionContext *context);
+Value __qmljs_builtin_post_increment_element(Value base, Value index, ExecutionContext *context);
+
+Value __qmljs_builtin_post_decrement(Value *val, ExecutionContext *ctx);
+Value __qmljs_builtin_post_decrement_name(String *name, ExecutionContext *context);
+Value __qmljs_builtin_post_decrement_member(Value base, String *name, ExecutionContext *context);
+Value __qmljs_builtin_post_decrement_element(Value base, Value index, ExecutionContext *context);
+
+void __qmljs_builtin_throw(Value val, ExecutionContext *context);
+void __qmljs_builtin_rethrow(ExecutionContext *context);
+ExecutionContext *__qmljs_builtin_push_with_scope(Value o, 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(Value object, String *name, Value val, ExecutionContext *ctx);
+void __qmljs_builtin_define_array_property(Value object, int index, Value val, ExecutionContext *ctx);
+void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx);
+
+// constructors
+Value __qmljs_init_closure(VM::Function *clos, ExecutionContext *ctx);
+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);
+
+Bool __qmljs_is_function(Value value);
+
+// string literals
+Value __qmljs_string_literal_undefined(ExecutionContext *ctx);
+Value __qmljs_string_literal_null(ExecutionContext *ctx);
+Value __qmljs_string_literal_true(ExecutionContext *ctx);
+Value __qmljs_string_literal_false(ExecutionContext *ctx);
+Value __qmljs_string_literal_object(ExecutionContext *ctx);
+Value __qmljs_string_literal_boolean(ExecutionContext *ctx);
+Value __qmljs_string_literal_number(ExecutionContext *ctx);
+Value __qmljs_string_literal_string(ExecutionContext *ctx);
+Value __qmljs_string_literal_function(ExecutionContext *ctx);
+
+// strings
+String *__qmljs_string_from_utf8(ExecutionContext *ctx, const char *s);
+int __qmljs_string_length(ExecutionContext *ctx, String *string);
+double __qmljs_string_to_number(const String *string);
+Value __qmljs_string_from_number(ExecutionContext *ctx, double number);
+Bool __qmljs_string_compare(ExecutionContext *ctx, String *left, String *right);
+Bool __qmljs_string_equal(String *left, String *right);
+String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second);
+String *__qmljs_identifier_from_utf8(ExecutionContext *ctx, const char *s);
+
+// objects
+Value __qmljs_object_default_value(ExecutionContext *ctx, Value object, int typeHint);
+Value __qmljs_throw_type_error(ExecutionContext *ctx);
+Value __qmljs_new_object(ExecutionContext *ctx);
+Value __qmljs_new_boolean_object(ExecutionContext *ctx, bool boolean);
+Value __qmljs_new_number_object(ExecutionContext *ctx, double n);
+Value __qmljs_new_string_object(ExecutionContext *ctx, String *string);
+void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value);
+void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Value value);
+Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name);
+Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name);
+
+Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index);
+void __qmljs_set_element(ExecutionContext *ctx, Value object, Value index, Value value);
+
+// For each
+Value __qmljs_foreach_iterator_object(Value in, ExecutionContext *ctx);
+Value __qmljs_foreach_next_property_name(Value foreach_iterator);
+
+// context
+Value __qmljs_get_thisObject(ExecutionContext *ctx);
+
+// type conversion and testing
+Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint);
+Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx);
+double __qmljs_to_number(Value value, ExecutionContext *ctx);
+double __qmljs_to_integer(Value value, ExecutionContext *ctx);
+int __qmljs_to_int32(Value value, ExecutionContext *ctx);
+unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx);
+Value __qmljs_to_string(Value value, ExecutionContext *ctx);
+Value __qmljs_to_object(Value value, ExecutionContext *ctx);
+//uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value);
+Bool __qmljs_is_callable(Value value, ExecutionContext *ctx);
+Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint);
+
+Bool __qmljs_equal(Value x, Value y, ExecutionContext *ctx);
+Bool __qmljs_strict_equal(Value x, Value y);
+
+// unary operators
+Value __qmljs_uplus(Value value, ExecutionContext *ctx);
+Value __qmljs_uminus(Value value, ExecutionContext *ctx);
+Value __qmljs_compl(Value value, ExecutionContext *ctx);
+Value __qmljs_not(Value value, ExecutionContext *ctx);
+Value __qmljs_increment(Value value, ExecutionContext *ctx);
+Value __qmljs_decrement(Value value, ExecutionContext *ctx);
+
+Value __qmljs_delete_subscript(ExecutionContext *ctx, Value base, Value index);
+Value __qmljs_delete_member(ExecutionContext *ctx, Value base, String *name);
+Value __qmljs_delete_name(ExecutionContext *ctx, String *name);
+
+void __qmljs_throw(Value value, ExecutionContext *context);
+// actually returns a jmp_buf *
+void *__qmljs_create_exception_handler(ExecutionContext *context);
+void __qmljs_delete_exception_handler(ExecutionContext *context);
+Value __qmljs_get_exception(ExecutionContext *context);
+
+// binary operators
+typedef Value (*BinOp)(Value left, Value right, ExecutionContext *ctx);
+
+Value __qmljs_instanceof(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_in(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_add(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_div(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_le(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_se(Value left, Value right, ExecutionContext *ctx);
+Value __qmljs_sne(Value left, Value right, ExecutionContext *ctx);
+
+Value __qmljs_add_helper(Value left, Value right, ExecutionContext *ctx);
+
+void __qmljs_inplace_bit_and_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_bit_or_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_bit_xor_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_add_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_sub_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_mul_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_div_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_mod_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_shl_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_shr_name(Value value, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_ushr_name(Value value, String *name, ExecutionContext *ctx);
+
+void __qmljs_inplace_bit_and_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_bit_or_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_bit_xor_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_add_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_sub_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_mul_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_div_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_mod_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_shl_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_shr_element(Value base, Value index, Value value, ExecutionContext *ctx);
+void __qmljs_inplace_ushr_element(Value base, Value index, Value value, ExecutionContext *ctx);
+
+void __qmljs_inplace_bit_and_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_bit_or_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_bit_xor_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_add_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_sub_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_mul_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_div_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_mod_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_shl_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_shr_member(Value value, Value base, String *name, ExecutionContext *ctx);
+void __qmljs_inplace_ushr_member(Value value, Value base, String *name, ExecutionContext *ctx);
+
+Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx);
+Bool __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx);
+
+// type conversion and testing
+inline Value __qmljs_to_primitive(Value value, ExecutionContext *ctx, int typeHint)
+{
+ if (!value.isObject())
+ return value;
+ return __qmljs_default_value(value, ctx, typeHint);
+}
+
+inline Bool __qmljs_to_boolean(Value value, ExecutionContext *ctx)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ case Value::Null_Type:
+ return false;
+ case Value::Boolean_Type:
+ case Value::Integer_Type:
+ return (bool)value.int_32;
+ case Value::String_Type:
+ return __qmljs_string_length(ctx, value.stringValue()) > 0;
+ case Value::Object_Type:
+ return true;
+ default: // double
+ if (! value.doubleValue() || std::isnan(value.doubleValue()))
+ return false;
+ return true;
+ }
+}
+
+inline double __qmljs_to_number(Value value, ExecutionContext *ctx)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ return 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, ctx, NUMBER_HINT);
+ return __qmljs_to_number(prim, ctx);
+ }
+ default: // double
+ return value.doubleValue();
+ }
+}
+
+inline double __qmljs_to_integer(Value value, ExecutionContext *ctx)
+{
+ if (value.isConvertibleToInt())
+ return value.int_32;
+
+ return Value::toInteger(__qmljs_to_number(value, ctx));
+}
+
+inline int __qmljs_to_int32(Value value, ExecutionContext *ctx)
+{
+ if (value.isConvertibleToInt())
+ return value.int_32;
+
+ return Value::toInt32(__qmljs_to_number(value, ctx));
+}
+
+inline unsigned short __qmljs_to_uint16(Value value, ExecutionContext *ctx)
+{
+ if (value.isConvertibleToInt())
+ return (ushort)(uint)value.integerValue();
+
+ double number = __qmljs_to_number(value, ctx);
+
+ 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;
+}
+
+inline Value __qmljs_to_string(Value value, ExecutionContext *ctx)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ return __qmljs_string_literal_undefined(ctx);
+ break;
+ case Value::Null_Type:
+ return __qmljs_string_literal_null(ctx);
+ break;
+ case Value::Boolean_Type:
+ if (value.booleanValue())
+ return __qmljs_string_literal_true(ctx);
+ else
+ return __qmljs_string_literal_false(ctx);
+ break;
+ case Value::String_Type:
+ return value;
+ break;
+ case Value::Object_Type: {
+ Value prim = __qmljs_to_primitive(value, ctx, STRING_HINT);
+ if (prim.isPrimitive())
+ return __qmljs_to_string(prim, ctx);
+ else
+ return __qmljs_throw_type_error(ctx);
+ break;
+ }
+ case Value::Integer_Type:
+ return __qmljs_string_from_number(ctx, value.int_32);
+ break;
+ default: // double
+ return __qmljs_string_from_number(ctx, value.doubleValue());
+ break;
+
+ } // switch
+}
+
+inline Value __qmljs_to_object(Value value, ExecutionContext *ctx)
+{
+ switch (value.type()) {
+ case Value::Undefined_Type:
+ case Value::Null_Type:
+ return __qmljs_throw_type_error(ctx);
+ break;
+ case Value::Boolean_Type:
+ return __qmljs_new_boolean_object(ctx, value.booleanValue());
+ break;
+ case Value::String_Type:
+ return __qmljs_new_string_object(ctx, value.stringValue());
+ break;
+ case Value::Object_Type:
+ return value;
+ break;
+ case Value::Integer_Type:
+ return __qmljs_new_number_object(ctx, value.int_32);
+ break;
+ default: // double
+ return __qmljs_new_number_object(ctx, value.doubleValue());
+ break;
+ }
+}
+
+/*
+inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, Value *value)
+{
+ switch (value->type()) {
+ case Value::Undefined_Type:
+ case Value::Null_Type:
+ *result = __qmljs_throw_type_error(ctx);
+ return false;
+ default:
+ return true;
+ }
+}
+*/
+
+inline Bool __qmljs_is_callable(Value value, ExecutionContext * /*ctx*/)
+{
+ if (value.isObject())
+ return __qmljs_is_function(value);
+ else
+ return false;
+}
+
+inline Value __qmljs_default_value(Value value, ExecutionContext *ctx, int typeHint)
+{
+ if (value.isObject())
+ return __qmljs_object_default_value(ctx, value, typeHint);
+ return Value::undefinedValue();
+}
+
+
+inline Value __qmljs_uplus(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ if (value.tryIntegerConversion())
+ return value;
+
+ double n = __qmljs_to_number(value, ctx);
+ return Value::fromDouble(n);
+}
+
+inline Value __qmljs_uminus(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ // +0 != -0, so we need to convert to double when negating 0
+ if (value.isInteger() && value.integerValue())
+ return Value::fromInt32(-value.integerValue());
+ double n = __qmljs_to_number(value, ctx);
+ return Value::fromDouble(-n);
+}
+
+inline Value __qmljs_compl(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ int n;
+ if (value.isConvertibleToInt())
+ n = value.int_32;
+ else
+ n = Value::toInt32(__qmljs_to_number(value, ctx));
+
+ return Value::fromInt32(~n);
+}
+
+inline Value __qmljs_not(Value value, ExecutionContext *ctx)
+{
+ TRACE1(value);
+
+ bool b = __qmljs_to_boolean(value, ctx);
+ return Value::fromBoolean(!b);
+}
+
+// binary operators
+inline Value __qmljs_bit_or(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right))
+ return Value::fromInt32(left.integerValue() | right.integerValue());
+
+ int lval = Value::toInt32(__qmljs_to_number(left, ctx));
+ int rval = Value::toInt32(__qmljs_to_number(right, ctx));
+ return Value::fromInt32(lval | rval);
+}
+
+inline Value __qmljs_bit_xor(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right))
+ return Value::fromInt32(left.integerValue() ^ right.integerValue());
+
+ int lval = Value::toInt32(__qmljs_to_number(left, ctx));
+ int rval = Value::toInt32(__qmljs_to_number(right, ctx));
+ return Value::fromInt32(lval ^ rval);
+}
+
+inline Value __qmljs_bit_and(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right))
+ return Value::fromInt32(left.integerValue() & right.integerValue());
+
+ int lval = Value::toInt32(__qmljs_to_number(left, ctx));
+ int rval = Value::toInt32(__qmljs_to_number(right, ctx));
+ return Value::fromInt32(lval & rval);
+}
+
+inline Value __qmljs_add(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+#ifndef QMLJS_LLVM_RUNTIME
+ if (Value::integerCompatible(left, right))
+ return add_int32(left.integerValue(), right.integerValue());
+#endif // QMLJS_LLVM_RUNTIME
+
+ if (Value::bothDouble(left, right))
+ return Value::fromDouble(left.doubleValue() + right.doubleValue());
+
+ return __qmljs_add_helper(left, right, ctx);
+}
+
+inline Value __qmljs_sub(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+#ifndef QMLJS_LLVM_RUNTIME
+ if (Value::integerCompatible(left, right))
+ return sub_int32(left.integerValue(), right.integerValue());
+#endif // QMLJS_LLVM_RUNTIME
+
+ double lval = __qmljs_to_number(left, ctx);
+ double rval = __qmljs_to_number(right, ctx);
+ return Value::fromDouble(lval - rval);
+}
+
+inline Value __qmljs_mul(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+#ifndef QMLJS_LLVM_RUNTIME
+ if (Value::integerCompatible(left, right))
+ return mul_int32(left.integerValue(), right.integerValue());
+#endif // QMLJS_LLVM_RUNTIME
+
+ double lval = __qmljs_to_number(left, ctx);
+ double rval = __qmljs_to_number(right, ctx);
+ return Value::fromDouble(lval * rval);
+}
+
+inline Value __qmljs_div(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ double lval = __qmljs_to_number(left, ctx);
+ double rval = __qmljs_to_number(right, ctx);
+ return Value::fromDouble(lval / rval);
+}
+
+inline Value __qmljs_mod(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right) && right.integerValue() != 0)
+ return Value::fromInt32(left.integerValue() % right.integerValue());
+
+ double lval = __qmljs_to_number(left, ctx);
+ double rval = __qmljs_to_number(right, ctx);
+ return Value::fromDouble(fmod(lval, rval));
+}
+
+inline Value __qmljs_shl(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right))
+ return Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f)));
+
+ int lval = Value::toInt32(__qmljs_to_number(left, ctx));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f;
+ return Value::fromInt32(lval << rval);
+}
+
+inline Value __qmljs_shr(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right))
+ return Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f)));
+
+ int lval = Value::toInt32(__qmljs_to_number(left, ctx));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f;
+ return Value::fromInt32(lval >> rval);
+}
+
+inline Value __qmljs_ushr(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ uint result;
+ if (Value::integerCompatible(left, right)) {
+ result = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f);
+ } else {
+ unsigned lval = Value::toUInt32(__qmljs_to_number(left, ctx));
+ unsigned rval = Value::toUInt32(__qmljs_to_number(right, ctx)) & 0x1f;
+ result = lval >> rval;
+ }
+
+ if (result > INT_MAX)
+ return Value::fromDouble(result);
+ return Value::fromInt32(result);
+}
+
+inline Value __qmljs_gt(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(__qmljs_cmp_gt(left, right, ctx));
+}
+
+inline Value __qmljs_lt(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(__qmljs_cmp_lt(left, right, ctx));
+}
+
+inline Value __qmljs_ge(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(__qmljs_cmp_ge(left, right, ctx));
+}
+
+inline Value __qmljs_le(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(__qmljs_cmp_le(left, right, ctx));
+}
+
+inline Value __qmljs_eq(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(__qmljs_cmp_eq(left, right, ctx));
+}
+
+inline Value __qmljs_ne(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return Value::fromBoolean(!__qmljs_cmp_eq(left, right, ctx));
+}
+
+inline Value __qmljs_se(Value left, Value right, ExecutionContext *)
+{
+ TRACE2(left, right);
+
+ bool r = __qmljs_strict_equal(left, right);
+ return Value::fromBoolean(r);
+}
+
+inline Value __qmljs_sne(Value left, Value right, ExecutionContext *)
+{
+ TRACE2(left, right);
+
+ bool r = ! __qmljs_strict_equal(left, right);
+ return Value::fromBoolean(r);
+}
+
+inline Bool __qmljs_cmp_gt(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ left = __qmljs_to_primitive(left, ctx, NUMBER_HINT);
+ right = __qmljs_to_primitive(right, ctx, NUMBER_HINT);
+
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() > right.integerValue();
+ if (Value::bothDouble(left, right)) {
+ return left.doubleValue() > right.doubleValue();
+ } else if (left.isString() && right.isString()) {
+ return __qmljs_string_compare(ctx, right.stringValue(), left.stringValue());
+ } else {
+ double l = __qmljs_to_number(left, ctx);
+ double r = __qmljs_to_number(right, ctx);
+ return l > r;
+ }
+}
+
+inline Bool __qmljs_cmp_lt(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ left = __qmljs_to_primitive(left, ctx, NUMBER_HINT);
+ right = __qmljs_to_primitive(right, ctx, NUMBER_HINT);
+
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() < right.integerValue();
+ if (Value::bothDouble(left, right)) {
+ return left.doubleValue() < right.doubleValue();
+ } else if (left.isString() && right.isString()) {
+ return __qmljs_string_compare(ctx, left.stringValue(), right.stringValue());
+ } else {
+ double l = __qmljs_to_number(left, ctx);
+ double r = __qmljs_to_number(right, ctx);
+ return l < r;
+ }
+}
+
+inline Bool __qmljs_cmp_ge(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ left = __qmljs_to_primitive(left, ctx, NUMBER_HINT);
+ right = __qmljs_to_primitive(right, ctx, NUMBER_HINT);
+
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() >= right.integerValue();
+ if (Value::bothDouble(left, right)) {
+ return left.doubleValue() >= right.doubleValue();
+ } else if (left.isString() && right.isString()) {
+ return !__qmljs_string_compare(ctx, left.stringValue(), right.stringValue());
+ } else {
+ double l = __qmljs_to_number(left, ctx);
+ double r = __qmljs_to_number(right, ctx);
+ return l >= r;
+ }
+}
+
+inline Bool __qmljs_cmp_le(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ left = __qmljs_to_primitive(left, ctx, NUMBER_HINT);
+ right = __qmljs_to_primitive(right, ctx, NUMBER_HINT);
+
+ if (Value::integerCompatible(left, right))
+ return left.integerValue() <= right.integerValue();
+ if (Value::bothDouble(left, right)) {
+ return left.doubleValue() <= right.doubleValue();
+ } else if (left.isString() && right.isString()) {
+ return !__qmljs_string_compare(ctx, right.stringValue(), left.stringValue());
+ } else {
+ double l = __qmljs_to_number(left, ctx);
+ double r = __qmljs_to_number(right, ctx);
+ return l <= r;
+ }
+}
+
+inline Bool __qmljs_cmp_eq(Value left, Value right, ExecutionContext *ctx)
+{
+ 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 __qmljs_string_equal(left.stringValue(), right.stringValue());
+
+ return __qmljs_equal(left, right, ctx);
+}
+
+inline Bool __qmljs_cmp_ne(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ return !__qmljs_cmp_eq(left, right, ctx);
+}
+
+inline Bool __qmljs_cmp_se(Value left, Value right, ExecutionContext *)
+{
+ TRACE2(left, right);
+
+ return __qmljs_strict_equal(left, right);
+}
+
+inline Bool __qmljs_cmp_sne(Value left, Value right, ExecutionContext *)
+{
+ TRACE2(left, right);
+
+ return ! __qmljs_strict_equal(left, right);
+}
+
+inline Bool __qmljs_cmp_instanceof(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ Value v = __qmljs_instanceof(left, right, ctx);
+ return v.booleanValue();
+}
+
+inline uint __qmljs_cmp_in(Value left, Value right, ExecutionContext *ctx)
+{
+ TRACE2(left, right);
+
+ Value v = __qmljs_in(left, right, ctx);
+ return v.booleanValue();
+}
+
+inline Bool __qmljs_strict_equal(Value x, Value y)
+{
+ TRACE2(x, y);
+
+ if (x.isDouble() || y.isDouble())
+ return x.asDouble() == y.asDouble();
+ if (x.rawValue() == y.rawValue())
+ return true;
+ if (x.isString() && y.isString())
+ return __qmljs_string_equal(x.stringValue(), y.stringValue());
+ return false;
+}
+
+} // extern "C"
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_RUNTIME_H
diff --git a/src/v4/qmljs_value.cpp b/src/v4/qmljs_value.cpp
new file mode 100644
index 0000000000..8dba13a9c9
--- /dev/null
+++ b/src/v4/qmljs_value.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** 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 <qmljs_engine.h>
+#include <qv4object.h>
+#include <qv4objectproto.h>
+
+namespace QQmlJS {
+namespace VM {
+
+
+int Value::toUInt16(ExecutionContext *ctx) const
+{
+ return __qmljs_to_uint16(*this, ctx);
+}
+
+Bool Value::toBoolean(ExecutionContext *ctx) const
+{
+ return __qmljs_to_boolean(*this, ctx);
+}
+
+double Value::toInteger(ExecutionContext *ctx) const
+{
+ return __qmljs_to_integer(*this, ctx);
+}
+
+double Value::toNumber(ExecutionContext *ctx) const
+{
+ return __qmljs_to_number(*this, ctx);
+}
+
+String *Value::toString(ExecutionContext *ctx) const
+{
+ Value v = __qmljs_to_string(*this, ctx);
+ assert(v.isString());
+ return v.stringValue();
+}
+
+Value Value::toObject(ExecutionContext *ctx) const
+{
+ return __qmljs_to_object(*this, ctx);
+}
+
+bool Value::sameValue(Value other) const {
+ if (val == other.val)
+ return true;
+ if (isString() && other.isString())
+ return stringValue()->isEqualTo(other.stringValue());
+ if (isInteger() && int_32 == 0 && other.dbl == 0)
+ return true;
+ if (dbl == 0 && other.isInteger() && other.int_32 == 0)
+ return true;
+ 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 (std::isnan(number))
+ return +0;
+ else if (! number || std::isinf(number))
+ return number;
+ const double v = floor(fabs(number));
+ return std::signbit(number) ? -v : v;
+}
+
+Value Value::property(ExecutionContext *ctx, String *name) const
+{
+ return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue();
+}
+
+
+
+} // namespace VM
+} // namespace QQmlJS
diff --git a/src/v4/qmljs_value.h b/src/v4/qmljs_value.h
new file mode 100644
index 0000000000..e11b08f50c
--- /dev/null
+++ b/src/v4/qmljs_value.h
@@ -0,0 +1,480 @@
+/****************************************************************************
+**
+** 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"
+
+namespace QQmlJS {
+namespace VM {
+
+struct String;
+struct ExecutionContext;
+struct ExecutionEngine;
+struct Value;
+
+extern "C" {
+double __qmljs_to_number(Value value, ExecutionContext *ctx);
+}
+
+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 CPU(X86_64)
+#else
+ 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,
+ 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
+
+ };
+
+ inline unsigned type() const {
+ return tag & Type_Mask;
+ }
+
+ 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 CPU(X86_64)
+ 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 CPU(X86_64)
+ 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 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(ExecutionContext *ctx) const;
+ int toInt32(ExecutionContext *ctx) const;
+ unsigned int toUInt32(ExecutionContext *ctx) const;
+
+ Bool toBoolean(ExecutionContext *ctx) const;
+ double toInteger(ExecutionContext *ctx) const;
+ double toNumber(ExecutionContext *ctx) const;
+ String *toString(ExecutionContext *ctx) const;
+ Value toObject(ExecutionContext *ctx) const;
+
+ inline bool isPrimitive() const { return !isObject(); }
+#if CPU(X86_64)
+ 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;
+ }
+
+ 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(ExecutionContext *ctx, 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 CPU(X86_64)
+ 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 CPU(X86_64)
+ v.val = quint64(_Null_Type) << Tag_Shift;
+#else
+ v.tag = _Null_Type;
+ v.int_32 = 0;
+#endif
+ 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 CPU(X86_64)
+ 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 CPU(X86_64)
+ v.val = (quint64)o;
+ v.val |= quint64(_Object_Type) << Tag_Shift;
+#else
+ v.tag = _Object_Type;
+ v.o = o;
+#endif
+ return v;
+}
+
+inline int Value::toInt32(ExecutionContext *ctx) const
+{
+ if (isConvertibleToInt())
+ return int_32;
+ double d;
+ if (isDouble())
+ d = dbl;
+ else
+ d = __qmljs_to_number(*this, ctx);
+
+ 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, ctx));
+}
+
+inline unsigned int Value::toUInt32(ExecutionContext *ctx) const {
+ if (isConvertibleToInt())
+ return (unsigned) int_32;
+ double d;
+ if (isDouble())
+ d = dbl;
+ else
+ d = __qmljs_to_number(*this, ctx);
+
+ 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(ExecutionContext *ctx, 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(ctx);
+ double d = toNumber(ctx);
+ if (d != idx) {
+ *ok = false;
+ return UINT_MAX;
+ }
+ return idx;
+}
+
+
+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;
+}
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif
diff --git a/src/v4/qv4_llvm_p.h b/src/v4/qv4_llvm_p.h
new file mode 100644
index 0000000000..60625c8677
--- /dev/null
+++ b/src/v4/qv4_llvm_p.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef QV4_LLVM_P_H
+#define QV4_LLVM_P_H
+
+#include "qv4global.h"
+#include "qv4ir_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/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp
new file mode 100644
index 0000000000..f7327506e9
--- /dev/null
+++ b/src/v4/qv4argumentsobject.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** 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 {
+
+
+ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount)
+ : context(context)
+{
+ 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, __qmljs_throw_type_error);
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower);
+ pd.configurable = PropertyDescriptor::Disabled;
+ pd.enumberable = PropertyDescriptor::Disabled;
+ __defineOwnProperty__(context, QStringLiteral("callee"), &pd);
+ __defineOwnProperty__(context, QStringLiteral("caller"), &pd);
+ } 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));
+ }
+ PropertyDescriptor pd;
+ pd.type = PropertyDescriptor::Data;
+ pd.writable = PropertyDescriptor::Enabled;
+ pd.configurable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) {
+ pd.value = context->argument(i);
+ __defineOwnProperty__(context, i, &pd);
+ }
+ defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function));
+ isNonStrictArgumentsObject = true;
+ }
+}
+
+bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc)
+{
+ PropertyDescriptor *pd = array.at(index);
+ PropertyDescriptor map;
+ bool isMapped = false;
+ if (pd && index < (uint)mappedArguments.size())
+ isMapped = pd->isAccessor() && pd->get == context->engine->argumentsAccessors.at(index).get;
+
+ if (isMapped) {
+ map = *pd;
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->configurable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Enabled;
+ pd->value = mappedArguments.at(index);
+ }
+
+ isNonStrictArgumentsObject = false;
+ bool strict = ctx->strictMode;
+ ctx->strictMode = false;
+ bool result = Object::__defineOwnProperty__(ctx, index, desc);
+ ctx->strictMode = strict;
+ isNonStrictArgumentsObject = true;
+
+ if (isMapped && desc->isData()) {
+ if (desc->type != PropertyDescriptor::Generic) {
+ Value arg = desc->value;
+ map.set->call(ctx, Value::fromObject(this), &arg, 1);
+ }
+ if (desc->writable != PropertyDescriptor::Disabled)
+ *pd = map;
+ }
+
+ if (ctx->strictMode && !result)
+ __qmljs_throw_type_error(ctx);
+ return result;
+}
+
+void ArgumentsObject::markObjects()
+{
+ for (int i = 0; i < mappedArguments.size(); ++i) {
+ Managed *m = mappedArguments.at(i).asManaged();
+ if (m)
+ m->mark();
+ }
+ Object::markObjects();
+}
+
+
+Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int)
+{
+ Object *that = thisObject.asObject();
+ if (!that)
+ __qmljs_throw_type_error(ctx);
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ __qmljs_throw_type_error(ctx);
+
+ assert(index < o->context->argumentCount);
+ return o->context->argument(index);
+}
+
+Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc)
+{
+ Object *that = thisObject.asObject();
+ if (!that)
+ __qmljs_throw_type_error(ctx);
+ ArgumentsObject *o = that->asArgumentsObject();
+ if (!o)
+ __qmljs_throw_type_error(ctx);
+
+ assert(index < o->context->argumentCount);
+ o->context->arguments[index] = argc ? args[0] : Value::undefinedValue();
+ return Value::undefinedValue();
+}
+
+
+}
+}
diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h
new file mode 100644
index 0000000000..adbaf5db1a
--- /dev/null
+++ b/src/v4/qv4argumentsobject.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct ArgumentsGetterFunction: FunctionObject
+{
+ uint index;
+
+ ArgumentsGetterFunction(ExecutionContext *scope, uint index)
+ : FunctionObject(scope), index(index) {}
+
+ virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int);
+};
+
+struct ArgumentsSetterFunction: FunctionObject
+{
+ uint index;
+
+ ArgumentsSetterFunction(ExecutionContext *scope, uint index)
+ : FunctionObject(scope), index(index) {}
+
+ virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc);
+};
+
+
+struct ArgumentsObject: Object {
+ ExecutionContext *context;
+ QVector<Value> mappedArguments;
+ ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount);
+
+ bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc);
+
+ virtual void markObjects();
+};
+
+}
+}
+
+#endif
+
diff --git a/src/v4/qv4array.cpp b/src/v4/qv4array.cpp
new file mode 100644
index 0000000000..9879b2f372
--- /dev/null
+++ b/src/v4/qv4array.cpp
@@ -0,0 +1,649 @@
+/****************************************************************************
+**
+** 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 "qv4array.h"
+#include "qmljs_runtime.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 PropertyDescriptor &p1, const PropertyDescriptor &p2) const
+{
+ if (p1.type == PropertyDescriptor::Generic)
+ return false;
+ if (p2.type == PropertyDescriptor::Generic)
+ return true;
+ Value v1 = thisObject->getValue(m_context, &p1);
+ Value v2 = thisObject->getValue(m_context, &p2);
+
+ if (v1.isUndefined())
+ return false;
+ if (v2.isUndefined())
+ return true;
+ if (!m_comparefn.isUndefined()) {
+ Value args[] = { v1, v2 };
+ Value result = __qmljs_call_value(m_context, Value::undefinedValue(), m_comparefn, args, 2);
+ return result.toNumber(m_context) <= 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);
+}
+
+Array::Array(const Array &other)
+ : len(other.len)
+ , lengthProperty(0)
+ , values(other.values)
+ , sparse(0)
+{
+ freeList = other.freeList;
+ if (other.sparse)
+ sparse = new SparseArray(*other.sparse);
+}
+
+
+Value Array::indexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o)
+{
+ bool protoHasArray = false;
+ Object *p = o;
+ while ((p = p->prototype))
+ if (p->array.length())
+ protoHasArray = true;
+
+ if (protoHasArray) {
+ // lets be safe and slow
+ for (uint i = fromIndex; i < endIndex; ++i) {
+ bool exists;
+ Value value = o->__get__(ctx, i, &exists);
+ if (exists && __qmljs_strict_equal(value, v))
+ return Value::fromDouble(i);
+ }
+ } else if (sparse) {
+ for (SparseArrayNode *n = sparse->lowerBound(fromIndex); n && n->key() < endIndex; n = n->nextNode()) {
+ bool exists;
+ Value value = o->getValueChecked(ctx, descriptor(n->value), &exists);
+ if (exists && __qmljs_strict_equal(value, v))
+ return Value::fromDouble(n->key());
+ }
+ } else {
+ if ((int) endIndex > values.size())
+ endIndex = values.size();
+ PropertyDescriptor *pd = values.data() + offset;
+ PropertyDescriptor *end = pd + endIndex;
+ pd += fromIndex;
+ while (pd < end) {
+ bool exists;
+ Value value = o->getValueChecked(ctx, pd, &exists);
+ if (exists && __qmljs_strict_equal(value, v))
+ return Value::fromDouble(pd - offset - values.constData());
+ ++pd;
+ }
+ }
+ return Value::fromInt32(-1);
+}
+
+void Array::concat(const Array &other)
+{
+ initSparse();
+ int newLen = len + other.length();
+ if (other.sparse)
+ initSparse();
+ if (sparse) {
+ if (other.sparse) {
+ for (const SparseArrayNode *it = other.sparse->begin(); it != other.sparse->end(); it = it->nextNode())
+ set(len + it->key(), other.descriptor(it->value));
+ } else {
+ int oldSize = values.size();
+ values.resize(oldSize + other.length());
+ memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor));
+ for (uint i = 0; i < other.length(); ++i) {
+ SparseArrayNode *n = sparse->insert(len + i);
+ n->value = oldSize + i;
+ }
+ }
+ } else {
+ int oldSize = values.size();
+ values.resize(oldSize + other.length());
+ memcpy(values.data() + oldSize, other.values.constData() + other.offset, other.length()*sizeof(PropertyDescriptor));
+ }
+ setLengthUnchecked(newLen);
+}
+
+void Array::sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len)
+{
+ if (sparse) {
+ context->throwUnimplemented("Array::sort unimplemented for sparse arrays");
+ return;
+ delete sparse;
+ }
+
+ ArrayElementLessThan lessThan(context, thisObject, comparefn);
+ if (len > values.size() - offset)
+ len = values.size() - offset;
+ PropertyDescriptor *begin = values.begin() + offset;
+ std::sort(begin, begin + len, lessThan);
+}
+
+
+void Array::initSparse()
+{
+ if (!sparse) {
+ sparse = new SparseArray;
+ for (int i = offset; i < values.size(); ++i) {
+ SparseArrayNode *n = sparse->insert(i - offset);
+ n->value = i;
+ }
+
+ if (offset) {
+ int o = offset;
+ for (int i = 0; i < o - 1; ++i) {
+ values[i].type = PropertyDescriptor::Generic;
+ values[i].value = Value::fromInt32(i + 1);
+ }
+ values[o - 1].type = PropertyDescriptor::Generic;
+ values[o - 1].value = Value::fromInt32(values.size());
+ freeList = 0;
+ } else {
+ freeList = values.size();
+ }
+ }
+}
+
+bool Array::setLength(uint newLen) {
+ if (lengthProperty && !lengthProperty->isWritable())
+ return false;
+ uint oldLen = length();
+ bool ok = true;
+ if (newLen < oldLen) {
+ if (sparse) {
+ SparseArrayNode *begin = sparse->lowerBound(newLen);
+ SparseArrayNode *it = sparse->end()->previousNode();
+ while (1) {
+ PropertyDescriptor &pd = values[it->value];
+ if (pd.type != PropertyDescriptor::Generic && !pd.isConfigurable()) {
+ ok = false;
+ newLen = it->key() + 1;
+ break;
+ }
+ pd.type = PropertyDescriptor::Generic;
+ pd.value.tag = Value::_Undefined_Type;
+ pd.value.int_32 = freeList;
+ freeList = it->value;
+ bool brk = (it == begin);
+ SparseArrayNode *prev = it->previousNode();
+ sparse->erase(it);
+ if (brk)
+ break;
+ it = prev;
+ }
+ } else {
+ PropertyDescriptor *it = values.data() + values.size();
+ const PropertyDescriptor *begin = values.constData() + offset + newLen;
+ while (--it >= begin) {
+ if (it->type != PropertyDescriptor::Generic && !it->isConfigurable()) {
+ ok = false;
+ newLen = it - values.data() + offset + 1;
+ break;
+ }
+ }
+ values.resize(newLen + offset);
+ }
+ } else {
+ if (newLen >= 0x100000)
+ initSparse();
+ }
+ setLengthUnchecked(newLen);
+ return ok;
+}
+
+void Array::markObjects() const
+{
+ uint i = sparse ? 0 : offset;
+ for (; i < (uint)values.size(); ++i) {
+ const PropertyDescriptor &pd = values.at(i);
+ if (pd.isData()) {
+ if (Managed *m = pd.value.asManaged())
+ m->mark();
+ } else if (pd.isAccessor()) {
+ if (pd.get)
+ pd.get->mark();
+ if (pd.set)
+ pd.set->mark();
+ }
+ }
+}
+
+}
+}
diff --git a/src/v4/qv4array.h b/src/v4/qv4array.h
new file mode 100644
index 0000000000..86d438e4e1
--- /dev/null
+++ b/src/v4/qv4array.h
@@ -0,0 +1,639 @@
+/****************************************************************************
+**
+** 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 <qmljs_value.h>
+#include <qv4propertydescriptor.h>
+#include <assert.h>
+
+#ifdef Q_MAP_DEBUG
+#include <QtCore/qdebug.h>
+#endif
+
+#include <new>
+
+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 PropertyDescriptor &v1, const PropertyDescriptor &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;
+}
+
+
+class Array
+{
+ friend struct ArrayPrototype;
+
+ uint len;
+ PropertyDescriptor *lengthProperty;
+ union {
+ uint freeList;
+ uint offset;
+ };
+ QVector<PropertyDescriptor> values;
+ SparseArray *sparse;
+
+ void fillDescriptor(PropertyDescriptor *pd, Value v)
+ {
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Enabled;
+ pd->configurable = PropertyDescriptor::Enabled;
+ pd->value = v;
+ }
+
+ uint allocValue() {
+ uint idx = freeList;
+ if (values.size() <= (int)freeList)
+ values.resize(++freeList);
+ else
+ freeList = values.at(freeList).value.integerValue();
+ return idx;
+ }
+
+ uint allocValue(Value v) {
+ uint idx = allocValue();
+ PropertyDescriptor *pd = &values[idx];
+ fillDescriptor(pd, v);
+ return idx;
+ }
+ void freeValue(int idx) {
+ PropertyDescriptor &pd = values[idx];
+ pd.type = PropertyDescriptor::Generic;
+ pd.value.tag = Value::_Undefined_Type;
+ pd.value.int_32 = freeList;
+ freeList = idx;
+ }
+
+ PropertyDescriptor *descriptor(uint index) {
+ PropertyDescriptor *pd = values.data() + index;
+ if (!sparse)
+ pd += offset;
+ return pd;
+ }
+ const PropertyDescriptor *descriptor(uint index) const {
+ const PropertyDescriptor *pd = values.data() + index;
+ if (!sparse)
+ pd += offset;
+ return pd;
+ }
+
+ void getHeadRoom() {
+ assert(!sparse && !offset);
+ offset = qMax(values.size() >> 2, 16);
+ QVector<PropertyDescriptor> newValues(values.size() + offset);
+ memcpy(newValues.data() + offset, values.constData(), values.size()*sizeof(PropertyDescriptor));
+ values = newValues;
+ }
+
+public:
+ Array() : len(0), lengthProperty(0), offset(0), sparse(0) {}
+ Array(const Array &other);
+ ~Array() { delete sparse; }
+ void initSparse();
+
+ uint length() const { return len; }
+ bool setLength(uint newLen);
+
+ void setLengthProperty(PropertyDescriptor *pd) { lengthProperty = pd; }
+ PropertyDescriptor *getLengthProperty() { return lengthProperty; }
+
+ void setLengthUnchecked(uint l) {
+ len = l;
+ if (lengthProperty)
+ lengthProperty->value = Value::fromUInt32(l);
+ }
+
+ PropertyDescriptor *insert(uint index) {
+ PropertyDescriptor *pd;
+ if (!sparse && (index < 0x1000 || index < len + (len >> 2))) {
+ if (index + offset >= (uint)values.size()) {
+ values.resize(offset + index + 1);
+ for (uint i = len + 1; i < index; ++i) {
+ values[i].type = PropertyDescriptor::Generic;
+ values[i].value.tag = Value::_Undefined_Type;
+ }
+ }
+ pd = descriptor(index);
+ } else {
+ initSparse();
+ SparseArrayNode *n = sparse->insert(index);
+ if (n->value == UINT_MAX)
+ n->value = allocValue();
+ pd = descriptor(n->value);
+ }
+ if (index >= len)
+ setLengthUnchecked(index + 1);
+ return pd;
+ }
+
+ void set(uint index, const PropertyDescriptor *pd) {
+ *insert(index) = *pd;
+ }
+
+ void set(uint index, Value value) {
+ PropertyDescriptor *pd = insert(index);
+ fillDescriptor(pd, value);
+ }
+
+ bool deleteIndex(uint index) {
+ if (index >= len)
+ return true;
+ PropertyDescriptor *pd = 0;
+ if (!sparse) {
+ pd = at(index);
+ } else {
+ SparseArrayNode *n = sparse->findNode(index);
+ if (n)
+ pd = descriptor(n->value);
+ }
+ if (!pd || pd->type == PropertyDescriptor::Generic)
+ return true;
+ if (!pd->isConfigurable())
+ return false;
+ pd->type = PropertyDescriptor::Generic;
+ pd->value.tag = Value::_Undefined_Type;
+ if (sparse) {
+ pd->value.int_32 = freeList;
+ freeList = pd - values.constData();
+ }
+ return true;
+ }
+
+ PropertyDescriptor *at(uint index) {
+ if (!sparse) {
+ if (index >= values.size() - offset)
+ return 0;
+ return values.data() + index + offset;
+ } else {
+ SparseArrayNode *n = sparse->findNode(index);
+ if (!n)
+ return 0;
+ return values.data() + n->value;
+ }
+ }
+
+ const PropertyDescriptor *nonSparseAt(uint index) const {
+ if (sparse)
+ return 0;
+ index += offset;
+ if (index >= (uint)values.size())
+ return 0;
+ return values.constData() + index;
+ }
+
+ PropertyDescriptor *nonSparseAtRef(uint index) {
+ if (sparse)
+ return 0;
+ index += offset;
+ if (index >= (uint)values.size())
+ return 0;
+ return values.data() + index;
+ }
+
+ const PropertyDescriptor *at(uint index) const {
+ if (!sparse) {
+ if (index >= values.size() - offset)
+ return 0;
+ return values.constData() + index + offset;
+ } else {
+ SparseArrayNode *n = sparse->findNode(index);
+ if (!n)
+ return 0;
+ return values.constData() + n->value;
+ }
+ }
+
+ void markObjects() const;
+
+ void push_front(Value v) {
+ if (!sparse) {
+ if (!offset)
+ getHeadRoom();
+
+ PropertyDescriptor pd;
+ fillDescriptor(&pd, v);
+ --offset;
+ values[offset] = pd;
+ } else {
+ uint idx = allocValue(v);
+ sparse->push_front(idx);
+ }
+ setLengthUnchecked(len + 1);
+ }
+ PropertyDescriptor *front() {
+ PropertyDescriptor *pd = 0;
+ if (!sparse) {
+ if (len)
+ pd = values.data() + offset;
+ } else {
+ SparseArrayNode *n = sparse->findNode(0);
+ if (n)
+ pd = descriptor(n->value);
+ }
+ if (pd && pd->type == PropertyDescriptor::Generic)
+ return 0;
+ return pd;
+ }
+ void pop_front() {
+ if (!len)
+ return;
+ if (!sparse) {
+ ++offset;
+ } else {
+ uint idx = sparse->pop_front();
+ freeValue(idx);
+ }
+ setLengthUnchecked(len - 1);
+ }
+ void push_back(Value v) {
+ if (!sparse) {
+ PropertyDescriptor pd;
+ fillDescriptor(&pd, v);
+ values.append(pd);
+ } else {
+ uint idx = allocValue(v);
+ sparse->push_back(idx, len);
+ }
+ setLengthUnchecked(len + 1);
+ }
+ PropertyDescriptor *back() {
+ PropertyDescriptor *pd = 0;
+ if (!sparse) {
+ if (len)
+ pd = values.data() + offset + len;
+ } else {
+ SparseArrayNode *n = sparse->findNode(len - 1);
+ if (n)
+ pd = descriptor(n->value);
+ }
+ if (pd && pd->type == PropertyDescriptor::Generic)
+ return 0;
+ return pd;
+ }
+ void pop_back() {
+ if (!len)
+ return;
+ if (!sparse) {
+ values.resize(values.size() - 1);
+ } else {
+ uint idx = sparse->pop_back(len);
+ if (idx != UINT_MAX)
+ freeValue(idx);
+ }
+ setLengthUnchecked(len - 1);
+ }
+
+ SparseArrayNode *sparseLowerBound(uint idx) { return sparse ? sparse->lowerBound(idx) : 0; }
+ SparseArrayNode *sparseBegin() { return sparse ? sparse->begin() : 0; }
+ SparseArrayNode *sparseEnd() { return sparse ? sparse->end() : 0; }
+
+ void concat(const Array &other);
+ void sort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len);
+ Value indexOf(Value v, uint fromIndex, uint len, ExecutionContext *ctx, Object *o);
+};
+
+}
+}
+
+#endif // QMAP_H
diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp
new file mode 100644
index 0000000000..cbf34d3077
--- /dev/null
+++ b/src/v4/qv4arrayobject.cpp
@@ -0,0 +1,798 @@
+/****************************************************************************
+**
+** 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 "qv4array.h"
+
+using namespace QQmlJS::VM;
+
+ArrayCtor::ArrayCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value ArrayCtor::call(ExecutionContext *ctx)
+{
+ ArrayObject *a = ctx->engine->newArrayObject(ctx);
+ Array &value = a->array;
+ if (ctx->argumentCount == 1 && ctx->argument(0).isNumber()) {
+ bool ok;
+ uint len = ctx->argument(0).asArrayLength(ctx, &ok);
+
+ if (!ok) {
+ ctx->throwRangeError(ctx->argument(0));
+ return Value::undefinedValue();
+ }
+
+ value.setLengthUnchecked(len);
+ } else {
+ for (unsigned int i = 0; i < ctx->argumentCount; ++i) {
+ value.set(i, ctx->argument(i));
+ }
+ }
+
+ return Value::fromObject(a);
+}
+
+void ArrayPrototype::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("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->array.length();
+ return o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx);
+}
+
+Value ArrayPrototype::method_isArray(ExecutionContext *ctx)
+{
+ Value arg = ctx->argument(0);
+ bool isArray = arg.asArrayObject();
+ return Value::fromBoolean(isArray);
+}
+
+Value ArrayPrototype::method_toString(ExecutionContext *ctx)
+{
+ return method_join(ctx);
+}
+
+Value ArrayPrototype::method_toLocaleString(ExecutionContext *ctx)
+{
+ return method_toString(ctx);
+}
+
+Value ArrayPrototype::method_concat(ExecutionContext *ctx)
+{
+ Array result;
+
+ if (ArrayObject *instance = ctx->thisObject.asArrayObject())
+ result = instance->array;
+ else {
+ QString v = ctx->thisObject.toString(ctx)->toQString();
+ result.set(0, Value::fromString(ctx, v));
+ }
+
+ for (uint i = 0; i < ctx->argumentCount; ++i) {
+ quint32 k = result.length();
+ Value arg = ctx->argument(i);
+
+ if (ArrayObject *elt = arg.asArrayObject())
+ result.concat(elt->array);
+
+ else
+ result.set(k, arg);
+ }
+
+ return Value::fromObject(ctx->engine->newArrayObject(ctx, result));
+}
+
+Value ArrayPrototype::method_join(ExecutionContext *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(ctx));
+
+ 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->array.length(); ++i) {
+ if (i)
+ R += r4;
+
+ Value e = a->__get__(ctx, i);
+ if (! (e.isUndefined() || e.isNull()))
+ R += e.toString(ctx)->toQString();
+ }
+ } else {
+ //
+ // crazy!
+ //
+ Value r6 = self.property(ctx, ctx->engine->identifier(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(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ 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->__get__(ctx, len - 1);
+
+ instance->__delete__(ctx, len - 1);
+ if (instance->isArrayObject())
+ instance->array.setLengthUnchecked(len - 1);
+ else
+ instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1));
+ return result;
+}
+
+Value ArrayPrototype::method_push(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ 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->array.length())
+ protoHasArray = true;
+
+ if (!protoHasArray && len == instance->array.length()) {
+ for (uint i = 0; i < ctx->argumentCount; ++i) {
+ Value v = ctx->argument(i);
+ instance->array.push_back(v);
+ }
+ } else {
+ for (uint i = 0; i < ctx->argumentCount; ++i)
+ instance->__put__(ctx, len + i, ctx->argument(i));
+ }
+ uint newLen = len + ctx->argumentCount;
+ if (!instance->isArrayObject())
+ 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_reverse(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ uint length = getLength(ctx, instance);
+
+ int lo = 0, hi = length - 1;
+
+ for (; lo < hi; ++lo, --hi) {
+ bool loExists, hiExists;
+ Value lval = instance->__get__(ctx, lo, &loExists);
+ Value hval = instance->__get__(ctx, hi, &hiExists);
+ if (hiExists)
+ instance->__put__(ctx, lo, hval);
+ else
+ instance->__delete__(ctx, lo);
+ if (loExists)
+ instance->__put__(ctx, hi, lval);
+ else
+ instance->__delete__(ctx, hi);
+ }
+ return Value::fromObject(instance);
+}
+
+Value ArrayPrototype::method_shift(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ 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->getValueChecked(ctx, instance->array.front());
+
+ bool protoHasArray = false;
+ Object *p = instance;
+ while ((p = p->prototype))
+ if (p->array.length())
+ protoHasArray = true;
+
+ if (!protoHasArray && len >= instance->array.length()) {
+ instance->array.pop_front();
+ } else {
+ // do it the slow way
+ for (uint k = 1; k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(ctx, k, &exists);
+ if (exists)
+ instance->__put__(ctx, k - 1, v);
+ else
+ instance->__delete__(ctx, k - 1);
+ }
+ instance->__delete__(ctx, len - 1);
+ }
+
+ if (!instance->isArrayObject())
+ instance->__put__(ctx, ctx->engine->id_length, Value::fromDouble(len - 1));
+ return result;
+}
+
+Value ArrayPrototype::method_slice(ExecutionContext *ctx)
+{
+ Object *o = ctx->thisObject.toObject(ctx).objectValue();
+
+ Array result;
+ uint len = o->__get__(ctx, ctx->engine->id_length).toUInt32(ctx);
+ double s = ctx->argument(0).toInteger(ctx);
+ 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(ctx);
+ 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->__get__(ctx, i, &exists);
+ if (exists)
+ result.set(n, v);
+ ++n;
+ }
+ return Value::fromObject(ctx->engine->newArrayObject(ctx, result));
+}
+
+Value ArrayPrototype::method_sort(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ Value comparefn = ctx->argument(0);
+ instance->array.sort(ctx, instance, comparefn, len);
+ return ctx->thisObject;
+}
+
+Value ArrayPrototype::method_splice(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ uint len = getLength(ctx, instance);
+
+ ArrayObject *newArray = ctx->engine->newArrayObject(ctx);
+
+ double rs = ctx->argument(0).toInteger(ctx);
+ 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(ctx), 0.), (double)(len - start));
+
+ newArray->array.values.resize(deleteCount);
+ PropertyDescriptor *pd = newArray->array.values.data();
+ for (uint i = 0; i < deleteCount; ++i) {
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Enabled;
+ pd->configurable = PropertyDescriptor::Enabled;
+ pd->value = instance->__get__(ctx, start + i);
+ ++pd;
+ }
+ newArray->array.setLengthUnchecked(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->__get__(ctx, k + deleteCount, &exists);
+ if (exists)
+ instance->array.set(k + itemCount, v);
+ else
+ instance->__delete__(ctx, k + itemCount);
+ }
+ for (uint k = len; k > len - deleteCount + itemCount; --k)
+ instance->__delete__(ctx, k - 1);
+ } else if (itemCount > deleteCount) {
+ uint k = len - deleteCount;
+ while (k > start) {
+ bool exists;
+ Value v = instance->__get__(ctx, k + deleteCount - 1, &exists);
+ if (exists)
+ instance->array.set(k + itemCount - 1, v);
+ else
+ instance->__delete__(ctx, k + itemCount - 1);
+ --k;
+ }
+ }
+
+ for (uint i = 0; i < itemCount; ++i)
+ instance->array.set(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(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ uint len = getLength(ctx, instance);
+
+ bool protoHasArray = false;
+ Object *p = instance;
+ while ((p = p->prototype))
+ if (p->array.length())
+ protoHasArray = true;
+
+ if (!protoHasArray && len >= instance->array.length()) {
+ for (int i = ctx->argumentCount - 1; i >= 0; --i) {
+ Value v = ctx->argument(i);
+ instance->array.push_front(v);
+ }
+ } else {
+ for (uint k = len; k > 0; --k) {
+ bool exists;
+ Value v = instance->__get__(ctx, k - 1, &exists);
+ if (exists)
+ instance->__put__(ctx, k + ctx->argumentCount - 1, v);
+ else
+ instance->__delete__(ctx, k + ctx->argumentCount - 1);
+ }
+ for (uint i = 0; i < ctx->argumentCount; ++i)
+ instance->__put__(ctx, i, ctx->argument(i));
+ }
+ uint newLen = len + ctx->argumentCount;
+ if (!instance->isArrayObject())
+ 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(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ 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(ctx);
+ 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->__get__(ctx, k, &exists);
+ if (exists && __qmljs_strict_equal(v, searchValue))
+ return Value::fromDouble(k);
+ }
+ return Value::fromInt32(-1);
+ }
+
+ return instance->array.indexOf(searchValue, fromIndex, len, ctx, instance);
+}
+
+Value ArrayPrototype::method_lastIndexOf(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ 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(ctx);
+ 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->__get__(ctx, k, &exists);
+ if (exists && __qmljs_strict_equal(v, searchValue))
+ return Value::fromDouble(k);
+ }
+ return Value::fromInt32(-1);
+}
+
+Value ArrayPrototype::method_every(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ Value thisArg = ctx->argument(1);
+
+ bool ok = true;
+ for (uint k = 0; ok && k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(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 = __qmljs_to_boolean(r, ctx);
+ }
+ return Value::fromBoolean(ok);
+}
+
+Value ArrayPrototype::method_some(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ Value thisArg = ctx->argument(1);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(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 (__qmljs_to_boolean(r, ctx))
+ return Value::fromBoolean(true);
+ }
+ return Value::fromBoolean(false);
+}
+
+Value ArrayPrototype::method_forEach(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ Value thisArg = ctx->argument(1);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(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(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ Value thisArg = ctx->argument(1);
+
+ ArrayObject *a = ctx->engine->newArrayObject(ctx);
+ a->array.setLength(len);
+
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(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->array.set(k, mapped);
+ }
+ return Value::fromObject(a);
+}
+
+Value ArrayPrototype::method_filter(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ Value thisArg = ctx->argument(1);
+
+ ArrayObject *a = ctx->engine->newArrayObject(ctx);
+
+ uint to = 0;
+ for (uint k = 0; k < len; ++k) {
+ bool exists;
+ Value v = instance->__get__(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 (__qmljs_to_boolean(selected, ctx)) {
+ a->array.set(to, v);
+ ++to;
+ }
+ }
+ return Value::fromObject(a);
+}
+
+Value ArrayPrototype::method_reduce(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ uint k = 0;
+ Value acc;
+ if (ctx->argumentCount > 1) {
+ acc = ctx->argument(1);
+ } else {
+ bool kPresent = false;
+ while (k < len && !kPresent) {
+ Value v = instance->__get__(ctx, k, &kPresent);
+ if (kPresent)
+ acc = v;
+ ++k;
+ }
+ if (!kPresent)
+ __qmljs_throw_type_error(ctx);
+ }
+
+ while (k < len) {
+ bool kPresent;
+ Value v = instance->__get__(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(ExecutionContext *ctx)
+{
+ Object *instance = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+
+ uint len = getLength(ctx, instance);
+
+ FunctionObject *callback = ctx->argument(0).asFunctionObject();
+ if (!callback)
+ __qmljs_throw_type_error(ctx);
+
+ if (len == 0) {
+ if (ctx->argumentCount == 1)
+ __qmljs_throw_type_error(ctx);
+ 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->__get__(ctx, k - 1, &kPresent);
+ if (kPresent)
+ acc = v;
+ --k;
+ }
+ if (!kPresent)
+ __qmljs_throw_type_error(ctx);
+ }
+
+ while (k > 0) {
+ bool kPresent;
+ Value v = instance->__get__(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/v4/qv4arrayobject.h b/src/v4/qv4arrayobject.h
new file mode 100644
index 0000000000..8a15546c1c
--- /dev/null
+++ b/src/v4/qv4arrayobject.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+
+struct ArrayCtor: FunctionObject
+{
+ ArrayCtor(ExecutionContext *scope);
+
+ virtual Value call(ExecutionContext *ctx);
+};
+
+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(ExecutionContext *ctx);
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_toLocaleString(ExecutionContext *ctx);
+ static Value method_concat(ExecutionContext *ctx);
+ static Value method_join(ExecutionContext *ctx);
+ static Value method_pop(ExecutionContext *ctx);
+ static Value method_push(ExecutionContext *ctx);
+ static Value method_reverse(ExecutionContext *ctx);
+ static Value method_shift(ExecutionContext *ctx);
+ static Value method_slice(ExecutionContext *ctx);
+ static Value method_sort(ExecutionContext *ctx);
+ static Value method_splice(ExecutionContext *ctx);
+ static Value method_unshift(ExecutionContext *ctx);
+ static Value method_indexOf(ExecutionContext *ctx);
+ static Value method_lastIndexOf(ExecutionContext *ctx);
+ static Value method_every(ExecutionContext *ctx);
+ static Value method_some(ExecutionContext *ctx);
+ static Value method_forEach(ExecutionContext *ctx);
+ static Value method_map(ExecutionContext *ctx);
+ static Value method_filter(ExecutionContext *ctx);
+ static Value method_reduce(ExecutionContext *ctx);
+ static Value method_reduceRight(ExecutionContext *ctx);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4booleanobject.cpp b/src/v4/qv4booleanobject.cpp
new file mode 100644
index 0000000000..9df0f23a6a
--- /dev/null
+++ b/src/v4/qv4booleanobject.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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;
+
+BooleanCtor::BooleanCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value BooleanCtor::construct(ExecutionContext *ctx)
+{
+ const double n = ctx->argument(0).toBoolean(ctx);
+ return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n)));
+}
+
+Value BooleanCtor::call(ExecutionContext *ctx)
+{
+ bool value = ctx->argumentCount ? ctx->argument(0).toBoolean(ctx) : 0;
+ return Value::fromBoolean(value);
+}
+
+void BooleanPrototype::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));
+ defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
+ defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf);
+}
+
+Value BooleanPrototype::method_toString(ExecutionContext *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(ExecutionContext *ctx)
+{
+ BooleanObject *thisObject = ctx->thisObject.asBooleanObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ return thisObject->value;
+}
diff --git a/src/v4/qv4booleanobject.h b/src/v4/qv4booleanobject.h
new file mode 100644
index 0000000000..44d87b1d50
--- /dev/null
+++ b/src/v4/qv4booleanobject.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct BooleanCtor: FunctionObject
+{
+ BooleanCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct BooleanPrototype: BooleanObject
+{
+ BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_valueOf(ExecutionContext *ctx);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4codegen.cpp b/src/v4/qv4codegen.cpp
new file mode 100644
index 0000000000..4c4f7dd1d6
--- /dev/null
+++ b/src/v4/qv4codegen.cpp
@@ -0,0 +1,2614 @@
+/****************************************************************************
+**
+** 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 "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 <qmljs_runtime.h>
+#include <qmljs_environment.h>
+#include <cmath>
+#include <iostream>
+#include <cassert>
+
+using namespace QQmlJS;
+using namespace AST;
+
+namespace {
+QTextStream qout(stdout, QIODevice::WriteOnly);
+
+void dfs(IR::BasicBlock *block,
+ QSet<IR::BasicBlock *> *V,
+ QVector<IR::BasicBlock *> *blocks)
+{
+ if (! V->contains(block)) {
+ V->insert(block);
+
+ foreach (IR::BasicBlock *succ, block->out)
+ dfs(succ, V, blocks);
+
+ blocks->append(block);
+ }
+}
+
+struct ComputeUseDef: IR::StmtVisitor, IR::ExprVisitor
+{
+ IR::Function *_function;
+ IR::Stmt *_stmt;
+
+ ComputeUseDef(IR::Function *function)
+ : _function(function)
+ , _stmt(0) {}
+
+ void operator()(IR::Stmt *s) {
+ assert(! s->d);
+ s->d = new IR::Stmt::Data;
+ qSwap(_stmt, s);
+ _stmt->accept(this);
+ qSwap(_stmt, s);
+ }
+
+ virtual void visitConst(IR::Const *) {}
+ virtual void visitString(IR::String *) {}
+ virtual void visitRegExp(IR::RegExp *) {}
+ virtual void visitName(IR::Name *) {}
+ virtual void visitClosure(IR::Closure *) {}
+ virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); }
+ virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
+ virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
+ virtual void visitMember(IR::Member *e) { e->base->accept(this); }
+ virtual void visitExp(IR::Exp *s) { s->expr->accept(this); }
+ virtual void visitEnter(IR::Enter *) {}
+ virtual void visitLeave(IR::Leave *) {}
+ virtual void visitJump(IR::Jump *) {}
+ virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
+ virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
+
+ virtual void visitTemp(IR::Temp *e) {
+ if (e->index < 0)
+ return;
+
+ if (! _stmt->d->uses.contains(e->index))
+ _stmt->d->uses.append(e->index);
+ }
+
+ virtual void visitCall(IR::Call *e) {
+ e->base->accept(this);
+ for (IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitNew(IR::New *e) {
+ e->base->accept(this);
+ for (IR::ExprList *it = e->args; it; it = it->next)
+ it->expr->accept(this);
+ }
+
+ virtual void visitMove(IR::Move *s) {
+ if (IR::Temp *t = s->target->asTemp()) {
+ if (t->index >= 0) {
+ if (! _stmt->d->defs.contains(t->index))
+ _stmt->d->defs.append(t->index);
+ }
+ } else {
+ s->target->accept(this);
+ }
+ s->source->accept(this);
+ }
+};
+
+void liveness(IR::Function *function)
+{
+ QSet<IR::BasicBlock *> V;
+ QVector<IR::BasicBlock *> blocks;
+
+ ComputeUseDef computeUseDef(function);
+ foreach (IR::BasicBlock *block, function->basicBlocks) {
+ foreach (IR::Stmt *s, block->statements)
+ computeUseDef(s);
+ }
+
+ dfs(function->basicBlocks.at(0), &V, &blocks);
+
+ bool changed;
+ do {
+ changed = false;
+
+ foreach (IR::BasicBlock *block, blocks) {
+ const QBitArray previousLiveIn = block->liveIn;
+ const QBitArray previousLiveOut = block->liveOut;
+ QBitArray live(function->tempCount);
+ foreach (IR::BasicBlock *succ, block->out)
+ live |= succ->liveIn;
+ block->liveOut = live;
+ for (int i = block->statements.size() - 1; i != -1; --i) {
+ IR::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);
+}
+
+} // end of anonymous namespace
+
+class Codegen::ScanFunctions: Visitor
+{
+public:
+ ScanFunctions(Codegen *cg)
+ : _cg(cg)
+ , _env(0)
+ {
+ }
+
+ 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)) {
+ if (strLit->value == QLatin1String("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(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(FunctionExpression *ast)
+ {
+ 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);
+ return true;
+ }
+
+ virtual void endVisit(FunctionExpression *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(PropertyGetterSetter *ast)
+ {
+ enterFunction(ast, QString(), ast->formals, ast->functionBody);
+ return true;
+ }
+
+ virtual void endVisit(PropertyGetterSetter *)
+ {
+ leaveEnvironment();
+ }
+
+ virtual bool visit(FunctionDeclaration *ast)
+ {
+ 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, ast);
+ return true;
+ }
+
+ virtual void endVisit(FunctionDeclaration *)
+ {
+ leaveEnvironment();
+ }
+
+ 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;
+ }
+
+private:
+ void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionDeclaration *decl = 0)
+ {
+ bool wasStrict = false;
+ if (_env) {
+ _env->hasNestedFunctions = true;
+ _env->enter(name, Environment::FunctionDefinition, decl);
+ if (name == QLatin1String("arguments"))
+ _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
+ wasStrict = _env->isStrict;
+ }
+
+ enterEnvironment(ast);
+ checkForArguments(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' 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;
+ }
+ }
+ }
+
+
+ Codegen *_cg;
+ Environment *_env;
+ QStack<Environment *> _envStack;
+};
+
+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)
+{
+}
+
+IR::Function *Codegen::operator()(const QString &fileName, Program *node,
+ IR::Module *module, Mode mode,
+ const QStringList &inheritedLocals)
+{
+ assert(node);
+
+ _fileName = fileName;
+ _module = module;
+ _env = 0;
+
+ ScanFunctions scan(this);
+ scan(node);
+
+ IR::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 (IR::Function *function, _module->functions) {
+ linearize(function);
+ }
+
+ qDeleteAll(_envMap);
+ _envMap.clear();
+
+ return globalCode;
+}
+
+IR::Function *Codegen::operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module)
+{
+ _fileName = fileName;
+ _module = module;
+ _env = 0;
+
+ ScanFunctions scan(this);
+ // fake a global environment
+ scan.enterEnvironment(0);
+ scan(ast);
+ scan.leaveEnvironment();
+
+ IR::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 (IR::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, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock)
+{
+ _loop = new Loop(node, breakBlock, continueBlock, _loop);
+ _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement
+ _loop->scopeAndFinally = _scopeAndFinally;
+ _labelledStatement = 0;
+}
+
+void Codegen::leaveLoop()
+{
+ Loop *current = _loop;
+ _loop = _loop->parent;
+ delete current;
+}
+
+IR::Expr *Codegen::member(IR::Expr *base, const QString *name)
+{
+ if (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);
+ }
+}
+
+IR::Expr *Codegen::subscript(IR::Expr *base, IR::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());
+}
+
+IR::Expr *Codegen::argument(IR::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
+IR::Expr *Codegen::reference(IR::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;
+}
+
+IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr)
+{
+ if (IR::Const *c = expr->asConst()) {
+ if (c->type == IR::NumberType) {
+ switch (op) {
+ case IR::OpNot:
+ return _block->CONST(IR::BoolType, !c->value);
+ case IR::OpUMinus:
+ return _block->CONST(IR::NumberType, -c->value);
+ case IR::OpUPlus:
+ return expr;
+ case IR::OpCompl:
+ return _block->CONST(IR::NumberType, ~VM::Value::toInt32(c->value));
+ case IR::OpIncrement:
+ return _block->CONST(IR::NumberType, c->value + 1);
+ case IR::OpDecrement:
+ return _block->CONST(IR::NumberType, c->value - 1);
+ 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());
+}
+
+IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right)
+{
+ if (IR::Const *c1 = left->asConst()) {
+ if (IR::Const *c2 = right->asConst()) {
+ if (c1->type == IR::NumberType && c2->type == IR::NumberType) {
+ switch (op) {
+ case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value);
+ case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0);
+ case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value));
+ case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value));
+ case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value));
+ case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value);
+ case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
+ case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
+ case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
+ case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
+ case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value);
+ case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value);
+ case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value);
+ case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value);
+ case IR::OpLShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f));
+ case IR::OpMod: return _block->CONST(IR::NumberType, ::fmod(c1->value, c2->value));
+ case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value);
+ case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value);
+ case IR::OpRShift: return _block->CONST(IR::NumberType, VM::Value::toInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f));
+ case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value);
+ case IR::OpURShift: return _block->CONST(IR::NumberType,VM::Value::toUInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f));
+
+ case IR::OpInstanceof:
+ case IR::OpIn:
+ assert(!"unreachabe");
+ break;
+
+ case IR::OpIfTrue: // unary ops
+ case IR::OpNot:
+ case IR::OpUMinus:
+ case IR::OpUPlus:
+ case IR::OpCompl:
+ case IR::OpIncrement:
+ case IR::OpDecrement:
+ case IR::OpInvalid:
+ break;
+ }
+ }
+ }
+ } else if (op == IR::OpAdd) {
+ if (IR::String *s1 = left->asString()) {
+ if (IR::String *s2 = right->asString()) {
+ return _block->STRING(_function->newString(*s1->value + *s2->value));
+ }
+ }
+ }
+
+ if (!left->asTemp() && !left->asConst()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), left);
+ left = _block->TEMP(t);
+ }
+
+ if (!right->asTemp() && !right->asConst()) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), right);
+ right = _block->TEMP(t);
+ }
+
+ assert(left->asTemp() || left->asConst());
+ assert(right->asTemp() || right->asConst());
+
+ return _block->BINOP(op, left, right);
+}
+
+IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args)
+{
+ base = reference(base);
+ return _block->CALL(base, args);
+}
+
+void Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
+{
+ assert(target->isLValue());
+
+ if (!source->asTemp() && !source->asConst() && (op != IR::OpInvalid || ! target->asTemp())) {
+ unsigned t = _block->newTemp();
+ _block->MOVE(_block->TEMP(t), source);
+ source = _block->TEMP(t);
+ }
+
+ _block->MOVE(target, source, op);
+}
+
+void Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::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, IR::BasicBlock *iftrue, IR::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)
+{
+ IR::Expr *initializer = 0;
+ if (!ast->expression)
+ return;
+ Result expr = expression(ast->expression);
+ assert(expr.code);
+ initializer = *expr;
+
+ if (! _env->parent || _function->insideWith) {
+ // 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)
+{
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Array"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn)));
+ int index = 0;
+ unsigned value = 0;
+ for (ElementList *it = ast->elements; it; it = it->next) {
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ ++index;
+ Result expr = expression(it->expression);
+
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ IR::ExprList *current = args;
+ current->expr = _block->TEMP(t);
+ current->next = _function->New<IR::ExprList>();
+ current = current->next;
+ current->expr = _block->CONST(IR::NumberType, index);
+ current->next = _function->New<IR::ExprList>();
+ current = current->next;
+
+ if (!value)
+ value = _block->newTemp();
+ move(_block->TEMP(value), *expr);
+ // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx)
+ current->expr = _block->TEMP(value);
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_define_array_property, 0, 0), args));
+
+ ++index;
+ }
+ if (ast->elision) {
+ for (Elision *elision = ast->elision->next; elision; elision = elision->next)
+ ++index;
+ // ### the new string leaks
+ move(member(_block->TEMP(t), _function->newString(QStringLiteral("length"))), _block->CONST(IR::NumberType, index + 1));
+ }
+ _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 IR::AluOp baseOp(int op)
+{
+ switch ((QSOperator::Op) op) {
+ case QSOperator::InplaceAnd: return IR::OpBitAnd;
+ case QSOperator::InplaceSub: return IR::OpSub;
+ case QSOperator::InplaceDiv: return IR::OpDiv;
+ case QSOperator::InplaceAdd: return IR::OpAdd;
+ case QSOperator::InplaceLeftShift: return IR::OpLShift;
+ case QSOperator::InplaceMod: return IR::OpMod;
+ case QSOperator::InplaceMul: return IR::OpMul;
+ case QSOperator::InplaceOr: return IR::OpBitOr;
+ case QSOperator::InplaceRightShift: return IR::OpRShift;
+ case QSOperator::InplaceURightShift: return IR::OpURShift;
+ case QSOperator::InplaceXor: return IR::OpBitXor;
+ default: return IR::OpInvalid;
+ }
+}
+
+bool Codegen::visit(BinaryExpression *ast)
+{
+ if (ast->op == QSOperator::And) {
+ if (_expr.accept(cx)) {
+ IR::BasicBlock *iftrue = _function->newBasicBlock();
+ condition(ast->left, iftrue, _expr.iffalse);
+ _block = iftrue;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ IR::BasicBlock *iftrue = _function->newBasicBlock();
+ IR::BasicBlock *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)) {
+ IR::BasicBlock *iffalse = _function->newBasicBlock();
+ condition(ast->left, _expr.iftrue, iffalse);
+ _block = iffalse;
+ condition(ast->right, _expr.iftrue, _expr.iffalse);
+ } else {
+ IR::BasicBlock *iffalse = _function->newBasicBlock();
+ IR::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;
+ }
+
+ IR::Expr* left = *expression(ast->left);
+ throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation());
+
+ switch (ast->op) {
+ case QSOperator::Or:
+ case QSOperator::And:
+ break;
+
+ case QSOperator::Assign: {
+ IR::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 = left;
+ }
+ 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: {
+ IR::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);
+ }
+
+ IR::Expr* right = *expression(ast->right);
+
+ if (_expr.accept(cx)) {
+ cjump(binop(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse);
+ } else {
+ IR::Expr *e = binop(IR::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);
+ }
+
+ IR::Expr* right = *expression(ast->right);
+
+ IR::Expr *e = binop(IR::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);
+ IR::ExprList *args = 0, **args_it = &args;
+ for (ArgumentList *it = ast->arguments; it; it = it->next) {
+ Result arg = expression(it->expression);
+ IR::Expr *actual = argument(*arg);
+ *args_it = _function->New<IR::ExprList>();
+ (*args_it)->init(actual);
+ args_it = &(*args_it)->next;
+ }
+ _expr.code = call(*base, args);
+ return false;
+}
+
+bool Codegen::visit(ConditionalExpression *ast)
+{
+ IR::BasicBlock *iftrue = _function->newBasicBlock();
+ IR::BasicBlock *iffalse = _function->newBasicBlock();
+ IR::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)
+{
+ IR::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(IR::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(IR::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(IR::BoolType, 1);
+ return false;
+ }
+ if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) {
+ _expr.code = _block->CONST(IR::BoolType, 1);
+ return false;
+ }
+
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(reference(expr));
+ _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args);
+ return false;
+}
+
+bool Codegen::visit(FalseLiteral *)
+{
+ if (_expr.accept(cx)) {
+ _block->JUMP(_expr.iffalse);
+ } else {
+ _expr.code = _block->CONST(IR::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)
+{
+ IR::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;
+}
+
+IR::Expr *Codegen::identifier(const QString &name, int line, int col)
+{
+ int index = _env->findMember(name);
+
+ if (! _function->hasDirectEval && !_function->insideWith && _env->parent) {
+ if (index != -1) {
+ return _block->TEMP(index);
+ }
+ index = indexOfArgument(&name);
+ if (index != -1) {
+ return _block->TEMP(-(index + 1));
+ }
+ }
+
+ if (index >= _env->members.size()) {
+ // named local variable, e.g. in a catch statement
+ return _block->TEMP(index);
+ }
+
+ 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);
+ IR::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);
+ IR::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);
+ }
+
+ IR::ExprList *args = 0, **args_it = &args;
+ for (ArgumentList *it = ast->arguments; it; it = it->next) {
+ Result arg = expression(it->expression);
+ IR::Expr *actual = argument(*arg);
+ *args_it = _function->New<IR::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(IR::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(IR::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(IR::NumberType, ast->value);
+ }
+ return false;
+}
+
+struct ObjectPropertyValue {
+ IR::Expr *value;
+ IR::Function *getter;
+ IR::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);
+ IR::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) {
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ IR::ExprList *current = args;
+ current->expr = _block->TEMP(t);
+ current->next = _function->New<IR::ExprList>();
+ current = current->next;
+ current->expr = _block->NAME(it.key(), 0, 0);
+ current->next = _function->New<IR::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(IR::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(IR::UndefinedType, 0));
+ move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(IR::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<IR::ExprList>();
+ current = current->next;
+ current->expr = _block->TEMP(setter);
+ _block->EXP(_block->CALL(_block->NAME(IR::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);
+
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(*expr);
+ _expr.code = call(_block->NAME(IR::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);
+
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(*expr);
+ _expr.code = call(_block->NAME(IR::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(IR::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(IR::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(IR::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(IR::BoolType, 1);
+ }
+ return false;
+}
+
+bool Codegen::visit(TypeOfExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(reference(*expr));
+ _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+ return false;
+}
+
+bool Codegen::visit(UnaryMinusExpression *ast)
+{
+ Result expr = expression(ast->expression);
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), unop(IR::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(IR::OpUPlus, *expr));
+ _expr.code = _block->TEMP(t);
+ return false;
+}
+
+bool Codegen::visit(VoidExpression *ast)
+{
+ statement(ast->expression);
+ _expr.code = _block->CONST(IR::UndefinedType, 0);
+ return false;
+}
+
+bool Codegen::visit(FunctionDeclaration * /*ast*/)
+{
+ _expr.accept(nx);
+ return false;
+}
+
+void Codegen::linearize(IR::Function *function)
+{
+ IR::BasicBlock *exitBlock = function->basicBlocks.last();
+ assert(exitBlock->isTerminated());
+ assert(exitBlock->terminator()->asRet());
+
+ QSet<IR::BasicBlock *> V;
+ V.insert(exitBlock);
+
+ QVector<IR::BasicBlock *> trace;
+
+ for (int i = 0; i < function->basicBlocks.size(); ++i) {
+ IR::BasicBlock *block = function->basicBlocks.at(i);
+ if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) {
+ IR::BasicBlock *next = function->basicBlocks.at(i + 1);
+ block->JUMP(next);
+ }
+ }
+
+ struct I { static void trace(IR::BasicBlock *block, QSet<IR::BasicBlock *> *V,
+ QVector<IR::BasicBlock *> *output) {
+ if (block == 0 || V->contains(block))
+ return;
+
+ V->insert(block);
+ block->index = output->size();
+ output->append(block);
+
+ if (IR::Stmt *term = block->terminator()) {
+ if (IR::Jump *j = term->asJump()) {
+ trace(j->target, V, output);
+ } else if (IR::CJump *cj = term->asCJump()) {
+ if (! V->contains(cj->iffalse))
+ trace(cj->iffalse, V, output);
+ else
+ trace(cj->iftrue, 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<IR::BasicBlock*> blocksToDelete;
+ foreach (IR::BasicBlock *b, function->basicBlocks)
+ if (!V.contains(b)) {
+ foreach (IR::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;
+
+#ifndef QV4_NO_LIVENESS
+ liveness(function);
+#endif
+
+ static bool showCode = !qgetenv("SHOW_CODE").isNull();
+ if (showCode) {
+ QVector<IR::Stmt *> code;
+ QHash<IR::Stmt *, IR::BasicBlock *> leader;
+
+ foreach (IR::BasicBlock *block, function->basicBlocks) {
+ leader.insert(block->statements.first(), block);
+ foreach (IR::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) {
+ IR::Stmt *s = code.at(i);
+
+ if (IR::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 (IR::BasicBlock *in, bb->in)
+ qout << " L" << in->index;
+ qout << endl;
+ }
+ IR::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, IR::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 (! 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;
+ }
+ }
+#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;
+ }
+}
+
+IR::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);
+ IR::Function *function = _module->newFunction(name, _function);
+
+ if (_debugger)
+ _debugger->addFunction(function);
+ IR::BasicBlock *entryBlock = function->newBasicBlock();
+ IR::BasicBlock *exitBlock = function->newBasicBlock(IR::Function::DontInsertBlock);
+ IR::BasicBlock *throwBlock = function->newBasicBlock();
+ function->hasDirectEval = _env->hasDirectEval;
+ function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed);
+ function->maxNumberOfArguments = _env->maxNumberOfArguments;
+ function->isStrict = _env->isStrict;
+
+ // 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, tempIndex, 0 };
+ _env->members.insert(inheritedLocal, member);
+ }
+ }
+
+ IR::ExprList *args = 0;
+ for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) {
+ const QString &local = it.key();
+ IR::ExprList *next = function->New<IR::ExprList>();
+ next->expr = entryBlock->NAME(local, 0, 0);
+ next->next = args;
+ args = next;
+ }
+ if (args) {
+ IR::ExprList *next = function->New<IR::ExprList>();
+ next->expr = entryBlock->CONST(IR::BoolType, mode == EvalCode);
+ next->next = args;
+ args = next;
+
+ entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args));
+ }
+ }
+
+ unsigned returnAddress = entryBlock->newTemp();
+
+ entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0));
+ exitBlock->RET(exitBlock->TEMP(returnAddress));
+ IR::ExprList *throwArgs = function->New<IR::ExprList>();
+ throwArgs->expr = throwBlock->TEMP(returnAddress);
+ throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
+ 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) {
+ IR::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;
+}
+
+int Codegen::indexOfArgument(const QStringRef &string) const
+{
+ for (int i = _function->formals.size() - 1; i >= 0; --i) {
+ if (*_function->formals.at(i) == string)
+ return i;
+ }
+ return -1;
+}
+
+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 *)
+{
+ assert(!"not implemented");
+ return false;
+}
+
+bool Codegen::visit(DoWhileStatement *ast)
+{
+ IR::BasicBlock *loopbody = _function->newBasicBlock();
+ IR::BasicBlock *loopcond = _function->newBasicBlock();
+ IR::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)
+{
+ IR::BasicBlock *foreachin = _function->newBasicBlock();
+ IR::BasicBlock *foreachbody = _function->newBasicBlock();
+ IR::BasicBlock *foreachend = _function->newBasicBlock();
+
+ enterLoop(ast, foreachend, foreachin);
+
+ int iterator = _block->newTemp();
+ move(_block->TEMP(iterator), *expression(ast->expression));
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+
+ _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<IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
+ int null = _block->newTemp();
+ move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
+ cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend);
+ _block = foreachend;
+
+ leaveLoop();
+ return false;
+}
+
+bool Codegen::visit(ForStatement *ast)
+{
+ IR::BasicBlock *forcond = _function->newBasicBlock();
+ IR::BasicBlock *forbody = _function->newBasicBlock();
+ IR::BasicBlock *forstep = _function->newBasicBlock();
+ IR::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)
+{
+ IR::BasicBlock *iftrue = _function->newBasicBlock();
+ IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0;
+ IR::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 {
+ IR::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)
+{
+ IR::BasicBlock *foreachin = _function->newBasicBlock();
+ IR::BasicBlock *foreachbody = _function->newBasicBlock();
+ IR::BasicBlock *foreachend = _function->newBasicBlock();
+
+ enterLoop(ast, foreachend, foreachin);
+
+ variableDeclaration(ast->declaration);
+
+ int iterator = _block->newTemp();
+ move(_block->TEMP(iterator), *expression(ast->expression));
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+
+ _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<IR::ExprList>();
+ args->init(_block->TEMP(iterator));
+ move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
+ int null = _block->newTemp();
+ move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
+ cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend);
+ _block = foreachend;
+
+ leaveLoop();
+ return false;
+}
+
+bool Codegen::visit(LocalForStatement *ast)
+{
+ IR::BasicBlock *forcond = _function->newBasicBlock();
+ IR::BasicBlock *forbody = _function->newBasicBlock();
+ IR::BasicBlock *forstep = _function->newBasicBlock();
+ IR::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)
+{
+ IR::BasicBlock *switchend = _function->newBasicBlock();
+
+ if (ast->block) {
+ Result lhs = expression(ast->expression);
+ IR::BasicBlock *switchcond = _block;
+
+ QHash<Node *, IR::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);
+ IR::BasicBlock *iftrue = blockMap[clause];
+ IR::BasicBlock *iffalse = _function->newBasicBlock();
+ cjump(binop(IR::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);
+ IR::BasicBlock *iftrue = blockMap[clause];
+ IR::BasicBlock *iffalse = _function->newBasicBlock();
+ cjump(binop(IR::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)
+{
+ 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"));
+
+ IR::BasicBlock *tryBody = _function->newBasicBlock();
+ IR::BasicBlock *catchBody = ast->catchExpression ? _function->newBasicBlock() : 0;
+ // We always need a finally body to clean up the exception handler
+ IR::BasicBlock *finallyBody = _function->newBasicBlock();
+
+ int inCatch = 0;
+ if (catchBody) {
+ inCatch = _block->newTemp();
+ move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, false));
+ }
+
+ int hasException = _block->newTemp();
+ move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_create_exception_handler, 0, 0), 0));
+
+ // Pass the hidden "inCatch" and "hasException" TEMPs to the
+ // builtin_delete_exception_handler, in order to have those TEMPs alive for
+ // the duration of the exception handling block.
+ IR::ExprList *deleteExceptionArgs = _function->New<IR::ExprList>();
+ deleteExceptionArgs->init(_block->TEMP(hasException));
+ if (inCatch) {
+ deleteExceptionArgs->next = _function->New<IR::ExprList>();
+ deleteExceptionArgs->next->init(_block->TEMP(inCatch));
+ }
+
+ ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, deleteExceptionArgs);
+ _scopeAndFinally = &tcf;
+
+ _block->CJUMP(_block->TEMP(hasException), catchBody ? catchBody : finallyBody, tryBody);
+
+ _block = tryBody;
+ statement(ast->statement);
+ _block->JUMP(finallyBody);
+
+ // regular flow does not go into the catch statement
+ if (catchBody) {
+ _block = catchBody;
+
+ if (inCatch != 0) {
+ // check if an exception got thrown within catch. Go to finally
+ // and then rethrow
+ IR::BasicBlock *b = _function->newBasicBlock();
+ _block->CJUMP(_block->TEMP(inCatch), finallyBody, b);
+ _block = b;
+ }
+ // if we have finally we need to clear the exception here, so we don't rethrow
+ move(_block->TEMP(inCatch), _block->CONST(IR::BoolType, true));
+ move(_block->TEMP(hasException), _block->CONST(IR::BoolType, false));
+
+ const int exception = _block->newTemp();
+ move(_block->TEMP(exception), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0));
+
+ // the variable used in the catch statement is local and hides any global
+ // variable with the same name.
+ const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 };
+ const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 };
+ Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember);
+ _env->members.insert(ast->catchExpression->name.toString(), catchMember);
+
+ statement(ast->catchExpression->statement);
+
+ // reset the variable name to the one from the outer scope
+ if (m.type == Environment::UndefinedMember)
+ _env->members.remove(ast->catchExpression->name.toString());
+ else
+ _env->members.insert(ast->catchExpression->name.toString(), m);
+ _block->JUMP(finallyBody);
+ }
+
+ _scopeAndFinally = tcf.parent;
+
+ IR::BasicBlock *after = _function->newBasicBlock();
+ _block = finallyBody;
+
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), deleteExceptionArgs));
+ int exception_to_rethrow = _block->newTemp();
+ move(_block->TEMP(exception_to_rethrow), _block->CALL(_block->NAME(IR::Name::builtin_get_exception, 0, 0), 0));
+
+ if (ast->finallyExpression && ast->finallyExpression->statement)
+ statement(ast->finallyExpression->statement);
+
+ IR::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)
+{
+ ScopeAndFinally *scopeAndFinally = _scopeAndFinally;
+ qSwap(_scopeAndFinally, scopeAndFinally);
+ while (_scopeAndFinally != outest) {
+ if (_scopeAndFinally->popScope) {
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0)));
+ _scopeAndFinally = _scopeAndFinally->parent;
+ } else {
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_delete_exception_handler, 0, 0), _scopeAndFinally->deleteExceptionArgs));
+ ScopeAndFinally *tc = _scopeAndFinally;
+ _scopeAndFinally = tc->parent;
+ if (tc->finally && tc->finally->statement)
+ statement(tc->finally->statement);
+ }
+ }
+ qSwap(_scopeAndFinally, scopeAndFinally);
+}
+
+bool Codegen::visit(VariableStatement *ast)
+{
+ variableDeclarationList(ast->declarations);
+ return false;
+}
+
+bool Codegen::visit(WhileStatement *ast)
+{
+ IR::BasicBlock *whilecond = _function->newBasicBlock();
+ IR::BasicBlock *whilebody = _function->newBasicBlock();
+ IR::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)
+{
+ IR::BasicBlock *withBlock = _function->newBasicBlock();
+
+ _block->JUMP(withBlock);
+ _block = withBlock;
+ int withObject = _block->newTemp();
+ _block->MOVE(_block->TEMP(withObject), *expression(ast->expression));
+ IR::ExprList *args = _function->New<IR::ExprList>();
+ args->init(_block->TEMP(withObject));
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args));
+
+ ++_function->insideWith;
+ {
+ ScopeAndFinally scope(_scopeAndFinally);
+ _scopeAndFinally = &scope;
+ statement(ast->statement);
+ _scopeAndFinally = scope.parent;
+ }
+ --_function->insideWith;
+ _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
+
+ IR::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(IR::Expr *expr, const SourceLocation& loc)
+{
+ if (!_env->isStrict)
+ return;
+ IR::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/v4/qv4codegen_p.h b/src/v4/qv4codegen_p.h
new file mode 100644
index 0000000000..d83e129782
--- /dev/null
+++ b/src/v4/qv4codegen_p.h
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** 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 "qv4ir_p.h"
+#include <private/qqmljsastvisitor_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
+ };
+
+ IR::Function *operator()(const QString &fileName, AST::Program *ast, IR::Module *module, Mode mode = GlobalCode, const QStringList &inheritedLocals = QStringList());
+ IR::Function *operator()(const QString &fileName, AST::FunctionExpression *ast, IR::Module *module);
+
+protected:
+ enum Format { ex, cx, nx };
+ struct Result {
+ IR::Expr *code;
+ IR::BasicBlock *iftrue;
+ IR::BasicBlock *iffalse;
+ Format format;
+ Format requested;
+
+ explicit Result(Format requested = ex)
+ : code(0)
+ , iftrue(0)
+ , iffalse(0)
+ , format(ex)
+ , requested(requested) {}
+
+ explicit Result(IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
+ : code(0)
+ , iftrue(iftrue)
+ , iffalse(iffalse)
+ , format(ex)
+ , requested(cx) {}
+
+ inline IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
+ inline IR::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::FunctionDeclaration *function;
+ };
+ typedef QMap<QString, Member> MemberMap;
+
+ MemberMap members;
+ int maxNumberOfArguments;
+ bool hasDirectEval;
+ bool hasNestedFunctions;
+ bool isStrict;
+ enum UsesArgumentsObject {
+ ArgumentsObjectUnknown,
+ ArgumentsObjectNotUsed,
+ ArgumentsObjectUsed
+ };
+
+ UsesArgumentsObject usesArgumentsObject;
+
+ Environment(Environment *parent)
+ : parent(parent)
+ , maxNumberOfArguments(0)
+ , hasDirectEval(false)
+ , hasNestedFunctions(false)
+ , isStrict(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::FunctionDeclaration *function = 0)
+ {
+ if (! name.isEmpty()) {
+ 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 {
+ ScopeAndFinally *parent;
+ AST::Finally *finally;
+ IR::ExprList *deleteExceptionArgs;
+ bool popScope;
+
+ ScopeAndFinally(ScopeAndFinally *parent) : parent(parent), finally(0), deleteExceptionArgs(0), popScope(true) {}
+ ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, IR::ExprList *deleteExceptionArgs)
+ : parent(parent), finally(finally), deleteExceptionArgs(deleteExceptionArgs), popScope(false)
+ {}
+ };
+
+ struct Loop {
+ AST::LabelledStatement *labelledStatement;
+ AST::Statement *node;
+ IR::BasicBlock *breakBlock;
+ IR::BasicBlock *continueBlock;
+ Loop *parent;
+ ScopeAndFinally *scopeAndFinally;
+
+ Loop(AST::Statement *node, IR::BasicBlock *breakBlock, IR::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, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock);
+ void leaveLoop();
+
+
+ IR::Expr *member(IR::Expr *base, const QString *name);
+ IR::Expr *subscript(IR::Expr *base, IR::Expr *index);
+ IR::Expr *argument(IR::Expr *expr);
+ IR::Expr *reference(IR::Expr *expr);
+ IR::Expr *unop(IR::AluOp op, IR::Expr *expr);
+ IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right);
+ IR::Expr *call(IR::Expr *base, IR::ExprList *args);
+ void move(IR::Expr *target, IR::Expr *source, IR::AluOp op = IR::OpInvalid);
+ void cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
+
+ void linearize(IR::Function *function);
+ IR::Function *defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::SourceElements *body,
+ Mode mode = FunctionCode,
+ const QStringList &inheritedLocals = QStringList());
+ int indexOfArgument(const QStringRef &string) const;
+
+ void unwindException(ScopeAndFinally *outest);
+
+ void statement(AST::Statement *ast);
+ void statement(AST::ExpressionNode *ast);
+ void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::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);
+
+ IR::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(IR::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;
+ IR::Module *_module;
+ IR::Function *_function;
+ IR::BasicBlock *_block;
+ IR::BasicBlock *_exitBlock;
+ IR::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/v4/qv4dateobject.cpp b/src/v4/qv4dateobject.cpp
new file mode 100644
index 0000000000..b9a4446087
--- /dev/null
+++ b/src/v4/qv4dateobject.cpp
@@ -0,0 +1,1313 @@
+/****************************************************************************
+**
+** 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 <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljsast_p.h>
+#include <qv4ir_p.h>
+#include <qv4codegen_p.h>
+#include <qv4isel_masm_p.h>
+
+#ifndef Q_WS_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;
+
+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)
+{
+#ifndef Q_WS_WIN
+ long int tt = (long int)(t / msPerSecond);
+ struct tm tmtm;
+ if (!localtime_r((const time_t*)&tt, &tmtm))
+ return 0;
+ return (tmtm.tm_isdst > 0) ? msPerHour : 0;
+#else
+ Q_UNUSED(t);
+ /// ### implement me
+ return 0;
+#endif
+}
+
+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_WS_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 (std::isnan(t))
+ return QDateTime();
+ if (spec == Qt::LocalTime)
+ t = LocalTime(t);
+ int year = int(YearFromTime(t));
+ int month = int(MonthFromTime(t) + 1);
+ int day = int(DateFromTime(t));
+ int hours = HourFromTime(t);
+ int mins = MinFromTime(t);
+ int secs = SecFromTime(t);
+ int ms = msFromTime(t);
+ return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec);
+}
+
+static inline QString ToString(double t)
+{
+ if (std::isnan(t))
+ return QStringLiteral("Invalid Date");
+ QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT");
+ double tzoffset = LocalTZA + DaylightSavingTA(t);
+ if (tzoffset) {
+ int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
+ int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
+ str.append(QLatin1Char((tzoffset > 0) ? '+' : '-'));
+ if (hours < 10)
+ str.append(QLatin1Char('0'));
+ str.append(QString::number(hours));
+ if (mins < 10)
+ str.append(QLatin1Char('0'));
+ str.append(QString::number(mins));
+ }
+ return str;
+}
+
+static inline QString ToUTCString(double t)
+{
+ if (std::isnan(t))
+ return QStringLiteral("Invalid Date");
+ return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT");
+}
+
+static inline QString ToDateString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).date().toString();
+}
+
+static inline QString ToTimeString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).time().toString();
+}
+
+static inline QString ToLocaleString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate);
+}
+
+static inline QString ToLocaleDateString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate);
+}
+
+static inline QString ToLocaleTimeString(double t)
+{
+ return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate);
+}
+
+static double getLocalTZA()
+{
+#ifndef Q_WS_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
+}
+
+
+DateCtor::DateCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value DateCtor::construct(ExecutionContext *ctx)
+{
+ double t = 0;
+
+ if (ctx->argumentCount == 0)
+ t = currentTime();
+
+ else if (ctx->argumentCount == 1) {
+ Value arg = ctx->argument(0);
+ if (DateObject *d = arg.asDateObject())
+ arg = d->value;
+ else
+ arg = __qmljs_to_primitive(arg, ctx, PREFERREDTYPE_HINT);
+
+ if (arg.isString())
+ t = ParseString(arg.stringValue()->toQString());
+ else
+ t = TimeClip(arg.toNumber(ctx));
+ }
+
+ else { // ctx->argumentCount > 1
+ double year = ctx->argument(0).toNumber(ctx);
+ double month = ctx->argument(1).toNumber(ctx);
+ double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1;
+ double hours = ctx->argumentCount >= 4 ? ctx->argument(3).toNumber(ctx) : 0;
+ double mins = ctx->argumentCount >= 5 ? ctx->argument(4).toNumber(ctx) : 0;
+ double secs = ctx->argumentCount >= 6 ? ctx->argument(5).toNumber(ctx) : 0;
+ double ms = ctx->argumentCount >= 7 ? ctx->argument(6).toNumber(ctx) : 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(ExecutionContext *ctx)
+{
+ 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(ExecutionContext *ctx)
+{
+ return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString()));
+}
+
+Value DatePrototype::method_UTC(ExecutionContext *ctx)
+{
+ const int numArgs = ctx->argumentCount;
+ if (numArgs >= 2) {
+ double year = ctx->argument(0).toNumber(ctx);
+ double month = ctx->argument(1).toNumber(ctx);
+ double day = numArgs >= 3 ? ctx->argument(2).toNumber(ctx) : 1;
+ double hours = numArgs >= 4 ? ctx->argument(3).toNumber(ctx) : 0;
+ double mins = numArgs >= 5 ? ctx->argument(4).toNumber(ctx) : 0;
+ double secs = numArgs >= 6 ? ctx->argument(5).toNumber(ctx) : 0;
+ double ms = numArgs >= 7 ? ctx->argument(6).toNumber(ctx) : 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(ExecutionContext *ctx)
+{
+ Q_UNUSED(ctx);
+ double t = currentTime();
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_toString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToString(t));
+}
+
+Value DatePrototype::method_toDateString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToDateString(t));
+}
+
+Value DatePrototype::method_toTimeString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToTimeString(t));
+}
+
+Value DatePrototype::method_toLocaleString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleString(t));
+}
+
+Value DatePrototype::method_toLocaleDateString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleDateString(t));
+}
+
+Value DatePrototype::method_toLocaleTimeString(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromString(ctx, ToLocaleTimeString(t));
+}
+
+Value DatePrototype::method_valueOf(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getTime(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getYear(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(LocalTime(t)) - 1900;
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getFullYear(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCFullYear(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = YearFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMonth(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MonthFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMonth(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MonthFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDate(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = DateFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDate(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = DateFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getDay(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = WeekDay(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCDay(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = WeekDay(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getHours(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = HourFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCHours(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = HourFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMinutes(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MinFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMinutes(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = MinFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getSeconds(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = SecFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCSeconds(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = SecFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getMilliseconds(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = msFromTime(LocalTime(t));
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getUTCMilliseconds(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = msFromTime(t);
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_getTimezoneOffset(ExecutionContext *ctx)
+{
+ double t = getThisDate(ctx);
+ if (! std::isnan(t))
+ t = (t - LocalTime(t)) / msPerMinute;
+ return Value::fromDouble(t);
+}
+
+Value DatePrototype::method_setTime(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ self->value.setDouble(TimeClip(ctx->argument(0).toNumber(ctx)));
+ return self->value;
+}
+
+Value DatePrototype::method_setMilliseconds(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double ms = ctx->argument(0).toNumber(ctx);
+ self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMilliseconds(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double ms = ctx->argument(0).toNumber(ctx);
+ self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ return self->value;
+}
+
+Value DatePrototype::method_setSeconds(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double sec = ctx->argument(0).toNumber(ctx);
+ double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCSeconds(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double sec = ctx->argument(0).toNumber(ctx);
+ double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setMinutes(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double min = ctx->argument(0).toNumber(ctx);
+ double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMinutes(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double min = ctx->argument(0).toNumber(ctx);
+ double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setHours(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double hour = ctx->argument(0).toNumber(ctx);
+ double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx);
+ double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCHours(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double hour = ctx->argument(0).toNumber(ctx);
+ double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx);
+ double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setDate(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double date = ctx->argument(0).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCDate(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double date = ctx->argument(0).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setMonth(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ double month = ctx->argument(0).toNumber(ctx);
+ double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCMonth(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double month = ctx->argument(0).toNumber(ctx);
+ double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setYear(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ if (std::isnan(t))
+ t = 0;
+ else
+ t = LocalTime(t);
+ double year = ctx->argument(0).toNumber(ctx);
+ double r;
+ if (std::isnan(year)) {
+ r = qSNaN();
+ } else {
+ if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99))
+ year += 1900;
+ r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
+ r = UTC(MakeDate(r, TimeWithinDay(t)));
+ r = TimeClip(r);
+ }
+ self->value.setDouble(r);
+ return self->value;
+}
+
+Value DatePrototype::method_setUTCFullYear(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = self->value.asDouble();
+ double year = ctx->argument(0).toNumber(ctx);
+ double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_setFullYear(ExecutionContext *ctx)
+{
+ DateObject *self = ctx->thisObject.asDateObject();
+ if (!self)
+ ctx->throwTypeError();
+
+ double t = LocalTime(self->value.asDouble());
+ if (std::isnan(t))
+ t = 0;
+ double year = ctx->argument(0).toNumber(ctx);
+ double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx);
+ double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx);
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ self->value.setDouble(t);
+ return self->value;
+}
+
+Value DatePrototype::method_toUTCString(ExecutionContext *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(ExecutionContext *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(ExecutionContext *ctx)
+{
+ Value O = __qmljs_to_object(ctx->thisObject, ctx);
+ Value tv = __qmljs_to_primitive(O, ctx, NUMBER_HINT);
+
+ if (tv.isNumber() && !std::isfinite(tv.toNumber(ctx)))
+ return Value::nullValue();
+
+ FunctionObject *toIso = O.objectValue()->__get__(ctx, ctx->engine->identifier(QStringLiteral("toISOString"))).asFunctionObject();
+
+ if (!toIso)
+ __qmljs_throw_type_error(ctx);
+
+ return toIso->call(ctx, ctx->thisObject, 0, 0);
+}
diff --git a/src/v4/qv4dateobject.h b/src/v4/qv4dateobject.h
new file mode 100644
index 0000000000..ede4473217
--- /dev/null
+++ b/src/v4/qv4dateobject.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct DateObject: Object {
+ Value value;
+ DateObject(const Value &value): value(value) { type = Type_DateObject; }
+};
+
+struct DateCtor: FunctionObject
+{
+ DateCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct DatePrototype: DateObject
+{
+ DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static double getThisDate(ExecutionContext *ctx);
+
+ static Value method_parse(ExecutionContext *ctx);
+ static Value method_UTC(ExecutionContext *ctx);
+ static Value method_now(ExecutionContext *ctx);
+
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_toDateString(ExecutionContext *ctx);
+ static Value method_toTimeString(ExecutionContext *ctx);
+ static Value method_toLocaleString(ExecutionContext *ctx);
+ static Value method_toLocaleDateString(ExecutionContext *ctx);
+ static Value method_toLocaleTimeString(ExecutionContext *ctx);
+ static Value method_valueOf(ExecutionContext *ctx);
+ static Value method_getTime(ExecutionContext *ctx);
+ static Value method_getYear(ExecutionContext *ctx);
+ static Value method_getFullYear(ExecutionContext *ctx);
+ static Value method_getUTCFullYear(ExecutionContext *ctx);
+ static Value method_getMonth(ExecutionContext *ctx);
+ static Value method_getUTCMonth(ExecutionContext *ctx);
+ static Value method_getDate(ExecutionContext *ctx);
+ static Value method_getUTCDate(ExecutionContext *ctx);
+ static Value method_getDay(ExecutionContext *ctx);
+ static Value method_getUTCDay(ExecutionContext *ctx);
+ static Value method_getHours(ExecutionContext *ctx);
+ static Value method_getUTCHours(ExecutionContext *ctx);
+ static Value method_getMinutes(ExecutionContext *ctx);
+ static Value method_getUTCMinutes(ExecutionContext *ctx);
+ static Value method_getSeconds(ExecutionContext *ctx);
+ static Value method_getUTCSeconds(ExecutionContext *ctx);
+ static Value method_getMilliseconds(ExecutionContext *ctx);
+ static Value method_getUTCMilliseconds(ExecutionContext *ctx);
+ static Value method_getTimezoneOffset(ExecutionContext *ctx);
+ static Value method_setTime(ExecutionContext *ctx);
+ static Value method_setMilliseconds(ExecutionContext *ctx);
+ static Value method_setUTCMilliseconds(ExecutionContext *ctx);
+ static Value method_setSeconds(ExecutionContext *ctx);
+ static Value method_setUTCSeconds(ExecutionContext *ctx);
+ static Value method_setMinutes(ExecutionContext *ctx);
+ static Value method_setUTCMinutes(ExecutionContext *ctx);
+ static Value method_setHours(ExecutionContext *ctx);
+ static Value method_setUTCHours(ExecutionContext *ctx);
+ static Value method_setDate(ExecutionContext *ctx);
+ static Value method_setUTCDate(ExecutionContext *ctx);
+ static Value method_setMonth(ExecutionContext *ctx);
+ static Value method_setUTCMonth(ExecutionContext *ctx);
+ static Value method_setYear(ExecutionContext *ctx);
+ static Value method_setFullYear(ExecutionContext *ctx);
+ static Value method_setUTCFullYear(ExecutionContext *ctx);
+ static Value method_toUTCString(ExecutionContext *ctx);
+ static Value method_toISOString(ExecutionContext *ctx);
+ static Value method_toJSON(ExecutionContext *ctx);
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4errorobject.cpp b/src/v4/qv4errorobject.cpp
new file mode 100644
index 0000000000..9d4a067f35
--- /dev/null
+++ b/src/v4/qv4errorobject.cpp
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** 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 <qv4ir_p.h>
+#include <qv4codegen_p.h>
+#include <qv4isel_masm_p.h>
+
+#ifndef Q_WS_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(ExecutionEngine* engine, const Value &message)
+ : errorType(Error)
+{
+ type = Type_ErrorObject;
+
+ if (message.type() != Value::Undefined_Type)
+ defineDefaultProperty(engine->identifier(QStringLiteral("message")), message);
+}
+
+void ErrorObject::setNameProperty(ExecutionContext *ctx)
+{
+ defineDefaultProperty(ctx, QLatin1String("name"), Value::fromString(ctx, className()));
+}
+
+SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message)
+ : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0))
+ , msg(message)
+{
+ errorType = SyntaxError;
+ prototype = ctx->engine->syntaxErrorPrototype;
+ setNameProperty(ctx);
+}
+
+
+
+EvalErrorObject::EvalErrorObject(ExecutionContext *ctx)
+ : ErrorObject(ctx->engine, ctx->argument(0))
+{
+ errorType = EvalError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->evalErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionContext *ctx)
+ : ErrorObject(ctx->engine, ctx->argument(0))
+{
+ errorType = RangeError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->rangeErrorPrototype;
+}
+
+RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &msg)
+ : ErrorObject(ctx->engine, Value::fromString(ctx,msg))
+{
+ errorType = RangeError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->rangeErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx)
+ : ErrorObject(ctx->engine, ctx->argument(0))
+{
+ errorType = ReferenceError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->referenceErrorPrototype;
+}
+
+ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &msg)
+ : ErrorObject(ctx->engine, Value::fromString(ctx,msg))
+{
+ errorType = ReferenceError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->referenceErrorPrototype;
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionContext *ctx)
+ : ErrorObject(ctx->engine, ctx->argument(0))
+{
+ errorType = TypeError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->typeErrorPrototype;
+}
+
+TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &msg)
+ : ErrorObject(ctx->engine, Value::fromString(ctx,msg))
+{
+ errorType = TypeError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->typeErrorPrototype;
+}
+
+URIErrorObject::URIErrorObject(ExecutionContext *ctx)
+ : ErrorObject(ctx->engine, ctx->argument(0))
+{
+ errorType = URIError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->uRIErrorPrototype;
+}
+
+URIErrorObject::URIErrorObject(ExecutionContext *ctx, Value msg)
+ : ErrorObject(ctx->engine, msg)
+{
+ errorType = URIError;
+ setNameProperty(ctx);
+ prototype = ctx->engine->uRIErrorPrototype;
+}
+
+
+ErrorCtor::ErrorCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value ErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(ctx->engine->newErrorObject(ctx->argument(0)));
+}
+
+Value ErrorCtor::call(ExecutionContext *ctx)
+{
+ return construct(ctx);
+}
+
+Value EvalErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx));
+}
+
+Value RangeErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx));
+}
+
+Value ReferenceErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx));
+}
+
+Value SyntaxErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0));
+}
+
+Value TypeErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx));
+}
+
+Value URIErrorCtor::construct(ExecutionContext *ctx)
+{
+ return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx));
+}
+
+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()));
+ obj->defineDefaultProperty(ctx, QStringLiteral("name"), Value::fromString(ctx, QStringLiteral("Error")));
+}
+
+Value ErrorPrototype::method_toString(ExecutionContext *ctx)
+{
+ Object *o = ctx->thisObject.asObject();
+ if (!o)
+ __qmljs_throw_type_error(ctx);
+
+ 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/v4/qv4errorobject.h b/src/v4/qv4errorobject.h
new file mode 100644
index 0000000000..d2d2340fae
--- /dev/null
+++ b/src/v4/qv4errorobject.h
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** 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"
+
+namespace QQmlJS {
+namespace VM {
+
+struct ErrorObject: Object {
+ enum ErrorType {
+ Error,
+ EvalError,
+ RangeError,
+ ReferenceError,
+ SyntaxError,
+ TypeError,
+ URIError
+ };
+ ErrorType errorType;
+
+ ErrorObject(ExecutionEngine* engine, const Value &message);
+
+ virtual struct SyntaxErrorObject *asSyntaxError() { return 0; }
+
+protected:
+ void setNameProperty(ExecutionContext *ctx);
+};
+
+struct EvalErrorObject: ErrorObject {
+ EvalErrorObject(ExecutionContext *ctx);
+};
+
+struct RangeErrorObject: ErrorObject {
+ RangeErrorObject(ExecutionContext *ctx);
+ RangeErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct ReferenceErrorObject: ErrorObject {
+ ReferenceErrorObject(ExecutionContext *ctx);
+ ReferenceErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct SyntaxErrorObject: ErrorObject {
+ SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg);
+ ~SyntaxErrorObject() { delete msg; }
+
+ virtual SyntaxErrorObject *asSyntaxError() { return this; }
+ DiagnosticMessage *message() { return msg; }
+
+private:
+ DiagnosticMessage *msg;
+};
+
+struct TypeErrorObject: ErrorObject {
+ TypeErrorObject(ExecutionContext *ctx);
+ TypeErrorObject(ExecutionContext *ctx, const QString &msg);
+};
+
+struct URIErrorObject: ErrorObject {
+ URIErrorObject(ExecutionContext *ctx);
+ URIErrorObject(ExecutionContext *ctx, Value);
+};
+
+struct ErrorCtor: FunctionObject
+{
+ ErrorCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct EvalErrorCtor: ErrorCtor
+{
+ EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct RangeErrorCtor: ErrorCtor
+{
+ RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct ReferenceErrorCtor: ErrorCtor
+{
+ ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct SyntaxErrorCtor: ErrorCtor
+{
+ SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct TypeErrorCtor: ErrorCtor
+{
+ TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct URIErrorCtor: ErrorCtor
+{
+ URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) {}
+
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+
+struct ErrorPrototype: ErrorObject
+{
+ // ### shouldn't be undefined
+ ErrorPrototype(ExecutionEngine* engine): ErrorObject(engine, 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(ExecutionContext *ctx);
+};
+
+struct EvalErrorPrototype: EvalErrorObject
+{
+ EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct RangeErrorPrototype: RangeErrorObject
+{
+ RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct ReferenceErrorPrototype: ReferenceErrorObject
+{
+ ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct SyntaxErrorPrototype: SyntaxErrorObject
+{
+ SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct TypeErrorPrototype: TypeErrorObject
+{
+ TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+struct URIErrorPrototype: URIErrorObject
+{
+ URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); }
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp
new file mode 100644
index 0000000000..f5e37def0c
--- /dev/null
+++ b/src/v4/qv4functionobject.cpp
@@ -0,0 +1,465 @@
+/****************************************************************************
+**
+** 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 "qv4ir_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 <qv4ir_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 <alloca.h>
+
+using namespace QQmlJS::VM;
+
+
+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)
+ : scope(scope)
+ , name(0)
+ , formalParameterList(0)
+ , varList(0)
+ , formalParameterCount(0)
+ , varCount(0)
+{
+ prototype = scope->engine->functionPrototype;
+
+ type = Type_FunctionObject;
+ needsActivation = false;
+ usesArgumentsObject = false;
+ strictMode = false;
+}
+
+bool FunctionObject::hasInstance(ExecutionContext *ctx, const Value &value)
+{
+ if (! value.isObject())
+ return false;
+
+ Value o = __get__(ctx, ctx->engine->id_prototype);
+ if (! o.isObject()) {
+ ctx->throwTypeError();
+ return false;
+ }
+
+ Object *v = value.objectValue();
+ while (v) {
+ v = v->prototype;
+
+ if (! v)
+ break;
+ else if (o.objectValue() == v)
+ return true;
+ }
+
+ return false;
+}
+
+Value FunctionObject::construct(ExecutionContext *context, Value *args, int argc)
+{
+ Object *obj = context->engine->newObject();
+ Value proto = __get__(context, context->engine->id_prototype);
+ if (proto.isObject())
+ obj->prototype = proto.objectValue();
+
+ uint size = requiredMemoryForExecutionContect(this, argc);
+ ExecutionContext *ctx = static_cast<ExecutionContext *>(needsActivation ? malloc(size) : alloca(size));
+
+ ctx->initCallContext(context, Value::fromObject(obj), this, args, argc);
+ Value result = construct(ctx);
+ ctx->leaveCallContext();
+
+ if (result.isObject())
+ return result;
+ return Value::fromObject(obj);
+}
+
+Value FunctionObject::call(ExecutionContext *context, Value thisObject, Value *args, int argc)
+{
+ uint size = requiredMemoryForExecutionContect(this, argc);
+ ExecutionContext *ctx = static_cast<ExecutionContext *>(needsActivation ? malloc(size) : alloca(size));
+
+
+ ctx->initCallContext(context, thisObject, this, args, argc);
+ if (isBuiltinFunction) {
+ // 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.isNull() || thisObject.isUndefined())
+ ctx->thisObject = thisObject;
+ }
+ Value result = call(ctx);
+ ctx->leaveCallContext();
+ return result;
+}
+
+void FunctionObject::markObjects()
+{
+ if (name)
+ 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();
+ scope->mark();
+ Object::markObjects();
+}
+
+Value FunctionObject::call(ExecutionContext *ctx)
+{
+ Q_UNUSED(ctx);
+ return Value::undefinedValue();
+}
+
+Value FunctionObject::construct(ExecutionContext *ctx)
+{
+ return call(ctx);
+}
+
+
+FunctionCtor::FunctionCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+// 15.3.2
+Value FunctionCtor::construct(ExecutionContext *ctx)
+{
+ MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager);
+
+ QString args;
+ QString body;
+ if (ctx->argumentCount > 0) {
+ for (uint i = 0; i < ctx->argumentCount - 1; ++i) {
+ if (i)
+ args += QLatin1String(", ");
+ args += ctx->argument(i).toString(ctx)->toQString();
+ }
+ body = ctx->argument(ctx->argumentCount - 1).toString(ctx)->toQString();
+ }
+
+ QString function = QLatin1String("function(") + args + 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);
+
+ IR::Module module;
+
+ Codegen cg(ctx, ctx->strictMode);
+ IR::Function *irf = cg(QString(), 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(ExecutionContext *ctx)
+{
+ return construct(ctx);
+}
+
+void FunctionPrototype::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));
+ 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);
+
+ defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0));
+}
+
+Value FunctionPrototype::method_toString(ExecutionContext *ctx)
+{
+ FunctionObject *fun = ctx->thisObject.asFunctionObject();
+ if (!fun)
+ ctx->throwTypeError();
+
+ return Value::fromString(ctx, QStringLiteral("function() { [code] }"));
+}
+
+Value FunctionPrototype::method_apply(ExecutionContext *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(ctx);
+ for (quint32 i = 0; i < len; ++i) {
+ Value a = arr->__get__(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(ExecutionContext *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(ExecutionContext *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, target, boundThis, boundArgs);
+ return Value::fromObject(f);
+}
+
+ScriptFunction::ScriptFunction(ExecutionContext *scope, VM::Function *function)
+ : FunctionObject(scope)
+ , 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));
+ PropertyDescriptor *pd = members->insert(scope->engine->id_prototype);
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Disabled;
+ pd->configurable = PropertyDescriptor::Disabled;
+ pd->value = Value::fromObject(proto);
+
+ if (scope->strictMode) {
+ FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error);
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower);
+ pd.configurable = PropertyDescriptor::Disabled;
+ pd.enumberable = PropertyDescriptor::Disabled;
+ __defineOwnProperty__(scope, QStringLiteral("caller"), &pd);
+ __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd);
+ }
+}
+
+ScriptFunction::~ScriptFunction()
+{
+}
+
+Value ScriptFunction::call(VM::ExecutionContext *ctx)
+{
+ assert(function->code);
+ return function->code(ctx, function->codeData);
+}
+
+void ScriptFunction::markObjects()
+{
+ function->mark();
+ FunctionObject::markObjects();
+}
+
+
+BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *))
+ : FunctionObject(scope)
+ , code(code)
+{
+ this->name = name;
+ isBuiltinFunction = true;
+}
+
+Value BuiltinFunctionOld::construct(ExecutionContext *ctx)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int))
+ : FunctionObject(scope)
+ , code(code)
+{
+ this->name = name;
+ isBuiltinFunction = true;
+}
+
+Value BuiltinFunction::construct(ExecutionContext *ctx)
+{
+ ctx->throwTypeError();
+ return Value::undefinedValue();
+}
+
+
+BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs)
+ : FunctionObject(scope)
+ , target(target)
+ , boundThis(boundThis)
+ , boundArgs(boundArgs)
+{
+ int len = target->__get__(scope, scope->engine->id_length).toUInt32(scope);
+ len -= boundArgs.size();
+ if (len < 0)
+ len = 0;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len));
+
+ FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, __qmljs_throw_type_error);
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower);
+ pd.configurable = PropertyDescriptor::Disabled;
+ pd.enumberable = PropertyDescriptor::Disabled;
+ *members->insert(scope->engine->id_arguments) = pd;
+ *members->insert(scope->engine->id_caller) = pd;
+}
+
+Value BoundFunction::call(ExecutionContext *context, Value, Value *args, int argc)
+{
+ Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(boundArgs.size() + argc)));
+ memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value));
+ memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value));
+
+ return target->call(context, boundThis, newArgs, boundArgs.size() + argc);
+}
+
+Value BoundFunction::construct(ExecutionContext *context, Value *args, int argc)
+{
+ Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(boundArgs.size() + argc)));
+ memcpy(newArgs, boundArgs.constData(), boundArgs.size()*sizeof(Value));
+ memcpy(newArgs + boundArgs.size(), args, argc*sizeof(Value));
+
+ return target->construct(context, newArgs, boundArgs.size() + argc);
+}
+
+bool BoundFunction::hasInstance(ExecutionContext *ctx, const Value &value)
+{
+ return target->hasInstance(ctx, value);
+}
+
+void BoundFunction::markObjects()
+{
+ target->mark();
+ if (Managed *m = boundThis.asManaged())
+ m->mark();
+ for (int i = 0; i < boundArgs.size(); ++i)
+ if (Managed *m = boundArgs.at(i).asManaged())
+ m->mark();
+ FunctionObject::markObjects();
+}
diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h
new file mode 100644
index 0000000000..b651eedcae
--- /dev/null
+++ b/src/v4/qv4functionobject.h
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qmljs_engine.h"
+#include "qmljs_environment.h"
+#include "qv4object.h"
+#include "qv4array.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4propertydescriptor.h"
+#include "qv4propertytable.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>
+
+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 Function {
+ String *name;
+
+ VM::Value (*code)(VM::ExecutionContext *, const uchar *);
+ const uchar *codeData;
+ JSC::MacroAssemblerCodeRef codeRef;
+
+ QVector<String *> formals;
+ QVector<String *> locals;
+ QVector<Value> generatedValues;
+ QVector<String *> identifiers;
+
+ bool hasNestedFunctions : 1;
+ bool hasDirectEval : 1;
+ bool usesArgumentsObject : 1;
+ bool isStrict : 1;
+
+ Function(String *name)
+ : name(name)
+ , code(0)
+ , codeData(0)
+ , hasNestedFunctions(0)
+ , hasDirectEval(false)
+ , usesArgumentsObject(false)
+ , isStrict(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;
+
+ FunctionObject(ExecutionContext *scope);
+
+ virtual bool hasInstance(ExecutionContext *ctx, const Value &value);
+
+ virtual Value construct(ExecutionContext *context, Value *args, int argc);
+ virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc);
+
+ virtual struct ScriptFunction *asScriptFunction() { return 0; }
+
+ virtual void markObjects();
+
+protected:
+ virtual Value call(ExecutionContext *ctx);
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct FunctionCtor: FunctionObject
+{
+ FunctionCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct FunctionPrototype: FunctionObject
+{
+ FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_apply(ExecutionContext *ctx);
+ static Value method_call(ExecutionContext *ctx);
+ static Value method_bind(ExecutionContext *ctx);
+};
+
+struct BuiltinFunctionOld: FunctionObject {
+ Value (*code)(ExecutionContext *);
+
+ BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *));
+ virtual Value call(ExecutionContext *ctx) { return code(ctx); }
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct BuiltinFunction: FunctionObject {
+ Value (*code)(ExecutionContext *parentContext, Value thisObject, Value *args, int argc);
+
+ BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *, Value, Value *, int));
+ virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc) {
+ return code(context, thisObject, args, argc);
+ }
+ virtual Value construct(ExecutionContext *ctx);
+};
+
+struct ScriptFunction: FunctionObject {
+ VM::Function *function;
+
+ ScriptFunction(ExecutionContext *scope, VM::Function *function);
+ virtual ~ScriptFunction();
+
+ virtual Value call(ExecutionContext *ctx);
+
+ virtual ScriptFunction *asScriptFunction() { return this; }
+
+ virtual void markObjects();
+};
+
+struct BoundFunction: FunctionObject {
+ FunctionObject *target;
+ Value boundThis;
+ QVector<Value> boundArgs;
+
+ BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs);
+ virtual ~BoundFunction() {}
+
+ virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc);
+ virtual Value construct(ExecutionContext *context, Value *args, int argc);
+ virtual bool hasInstance(ExecutionContext *ctx, const Value &value);
+ virtual void markObjects();
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/v4/qv4global.h b/src/v4/qv4global.h
new file mode 100644
index 0000000000..6b1c9a21ec
--- /dev/null
+++ b/src/v4/qv4global.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef QV4GLOBAL_H
+#define QV4GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+#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
+
+QT_END_NAMESPACE
+
+#endif // QV4GLOBAL_H
diff --git a/src/v4/qv4globalobject.cpp b/src/v4/qv4globalobject.cpp
new file mode 100644
index 0000000000..74cd32d88a
--- /dev/null
+++ b/src/v4/qv4globalobject.cpp
@@ -0,0 +1,670 @@
+/****************************************************************************
+**
+** 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 "qmljs_value.h"
+#include "qmljs_environment.h"
+
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljsast_p.h>
+#include <qv4ir_p.h>
+#include <qv4codegen_p.h>
+#include "private/qlocale_tools_p.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+#include <iostream>
+#include <alloca.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[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()";
+
+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 QString &unescapedSet, bool *ok)
+{
+ *ok = true;
+ QString output;
+ const int length = input.length();
+ int i = 0;
+ while (i < length) {
+ const QChar c = input.at(i);
+ if (!unescapedSet.contains(c)) {
+ 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;
+}
+
+static QString decode(const QString &input, const QString &reservedSet, bool *ok)
+{
+ *ok = true;
+ QString output;
+ 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 {
+ QChar z(b);
+ if (!reservedSet.contains(z)) {
+ output.append(z);
+ } else {
+ output.append(input.mid(start, i - start + 1));
+ }
+ }
+ } else {
+ output.append(ch);
+ }
+ ++i;
+ }
+ if (i != length)
+ *ok = false;
+ return output;
+ error:
+ *ok = false;
+ return QString();
+}
+
+
+EvalFunction::EvalFunction(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+ name = scope->engine->id_eval;
+ defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1));
+}
+
+Value EvalFunction::evalCall(ExecutionContext *context, Value /*thisObject*/, Value *args, int argc, bool directCall)
+{
+ if (argc < 1)
+ return Value::undefinedValue();
+
+ ExecutionContext *ctx = context;
+
+ if (!directCall) {
+ // the context for eval should be the global scope
+ while (ctx->parent)
+ ctx = ctx->parent;
+ }
+
+ if (!args[0].isString())
+ return args[0];
+
+ const QString code = args[0].stringValue()->toQString();
+ bool inheritContext = !ctx->strictMode;
+
+ QQmlJS::VM::Function *f = parseSource(context, QStringLiteral("eval code"),
+ code, QQmlJS::Codegen::EvalCode,
+ (directCall && context->strictMode), inheritContext);
+
+ if (!f)
+ return Value::undefinedValue();
+
+ bool strict = f->isStrict || (directCall && context->strictMode);
+
+ uint size = requiredMemoryForExecutionContect(this, argc);
+ ExecutionContext *k = static_cast<ExecutionContext *>(alloca(size));
+
+ if (strict) {
+ ctx = k;
+ ctx->initCallContext(context, context->thisObject, this, args, argc);
+ }
+
+ // set the correct strict mode flag on the context
+ bool cstrict = ctx->strictMode;
+ ctx->strictMode = strict;
+
+ Value result = f->code(ctx, f->codeData);
+
+ ctx->strictMode = cstrict;
+
+ if (strict)
+ ctx->leaveCallContext();
+
+ return result;
+}
+
+
+Value EvalFunction::call(ExecutionContext *context, Value thisObject, Value *args, int argc)
+{
+ // indirect call
+ return evalCall(context, thisObject, args, argc, false);
+}
+
+Value EvalFunction::construct(ExecutionContext *ctx)
+{
+ 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;
+ IR::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);
+ IR::Function *globalIRCode = cg(fileName, program, &module, mode, inheritedLocals);
+ QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(vm, &module));
+ if (globalIRCode)
+ globalCode = isel->vmFunction(globalIRCode);
+ }
+
+ if (! globalCode)
+ // ### should be a syntax error
+ __qmljs_throw_type_error(ctx);
+ }
+
+ 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(ExecutionContext *context)
+{
+ Value string = context->argument(0);
+ Value radix = context->argument(1);
+ int R = radix.isUndefined() ? 0 : radix.toInt32(context);
+
+ // [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(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(nan(""));
+ bool overflow = false;
+ qint64 v_overflow;
+ unsigned overflow_digit_count = 0;
+ int d = toInt(*pos++, R);
+ if (d == -1)
+ return Value::fromDouble(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(ExecutionContext *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(INFINITY);
+ if (trimmed.startsWith("-Infinity"))
+ return Value::fromDouble(-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(nan("")); // 3
+ else
+ return Value::fromDouble(d);
+}
+
+/// isNaN [15.1.2.4]
+Value GlobalFunctions::method_isNaN(ExecutionContext *context)
+{
+ const Value &v = context->argument(0);
+ if (v.integerCompatible())
+ return Value::fromBoolean(false);
+
+ double d = v.toNumber(context);
+ return Value::fromBoolean(std::isnan(d));
+}
+
+/// isFinite [15.1.2.5]
+Value GlobalFunctions::method_isFinite(ExecutionContext *context)
+{
+ const Value &v = context->argument(0);
+ if (v.integerCompatible())
+ return Value::fromBoolean(true);
+
+ double d = v.toNumber(context);
+ return Value::fromBoolean(std::isfinite(d));
+}
+
+/// decodeURI [15.1.3.1]
+Value GlobalFunctions::method_decodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ if (argc == 0)
+ return Value::undefinedValue();
+
+ QString uriString = argv[0].toString(parentCtx)->toQString();
+ bool ok;
+ QString out = decode(uriString, QString::fromUtf8(uriReserved) + QString::fromUtf8("#"), &ok);
+ if (!ok)
+ parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(parentCtx, out);
+}
+
+/// decodeURIComponent [15.1.3.2]
+Value GlobalFunctions::method_decodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ if (argc == 0)
+ return Value::undefinedValue();
+
+ QString uriString = argv[0].toString(parentCtx)->toQString();
+ bool ok;
+ QString out = decode(uriString, QString(), &ok);
+ if (!ok)
+ parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(parentCtx, out);
+}
+
+/// encodeURI [15.1.3.3]
+Value GlobalFunctions::method_encodeURI(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ if (argc == 0)
+ return Value::undefinedValue();
+
+ QString uriString = argv[0].toString(parentCtx)->toQString();
+ bool ok;
+ QString out = encode(uriString, QLatin1String(uriReserved) + QLatin1String(uriUnescaped) + QString::fromUtf8("#"), &ok);
+ if (!ok)
+ parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(parentCtx, out);
+}
+
+/// encodeURIComponent [15.1.3.4]
+Value GlobalFunctions::method_encodeURIComponent(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ if (argc == 0)
+ return Value::undefinedValue();
+
+ QString uriString = argv[0].toString(parentCtx)->toQString();
+ bool ok;
+ QString out = encode(uriString, QString(uriUnescaped), &ok);
+ if (!ok)
+ parentCtx->throwURIError(Value::fromString(parentCtx, QStringLiteral("malformed URI sequence")));
+
+ return Value::fromString(parentCtx, out);
+}
+
+Value GlobalFunctions::method_escape(ExecutionContext *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(ExecutionContext *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/v4/qv4globalobject.h b/src/v4/qv4globalobject.h
new file mode 100644
index 0000000000..6a586c1802
--- /dev/null
+++ b/src/v4/qv4globalobject.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 QV4GLOBALOBJECT_H
+#define QV4GLOBALOBJECT_H
+
+#include "qv4global.h"
+#include "qv4functionobject.h"
+
+namespace QQmlJS {
+
+namespace VM {
+
+struct Q_V4_EXPORT EvalFunction : FunctionObject
+{
+ EvalFunction(ExecutionContext *scope);
+
+ static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx,
+ const QString &fileName,
+ const QString &source,
+ QQmlJS::Codegen::Mode mode, bool strictMode,
+ bool inheritContext);
+
+ virtual Value call(ExecutionContext *context, Value thisObject, Value *args, int argc);
+ Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall);
+
+ Value construct(ExecutionContext *ctx);
+};
+
+struct GlobalFunctions
+{
+ static Value method_parseInt(ExecutionContext *context);
+ static Value method_parseFloat(ExecutionContext *context);
+ static Value method_isNaN(ExecutionContext *context);
+ static Value method_isFinite(ExecutionContext *context);
+ static Value method_decodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_decodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_encodeURI(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_encodeURIComponent(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_escape(ExecutionContext *context);
+ static Value method_unescape(ExecutionContext *context);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/v4/qv4ir.cpp b/src/v4/qv4ir.cpp
new file mode 100644
index 0000000000..1c2a9d9e62
--- /dev/null
+++ b/src/v4/qv4ir.cpp
@@ -0,0 +1,670 @@
+/****************************************************************************
+**
+** 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 "qv4ir_p.h"
+#include <private/qqmljsast_p.h>
+
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+#include <cmath>
+#include <cassert>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace IR {
+
+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;
+ }
+}
+
+void Const::dump(QTextStream &out)
+{
+ switch (type) {
+ case QQmlJS::IR::UndefinedType:
+ out << "undefined";
+ break;
+ case QQmlJS::IR::NullType:
+ out << "null";
+ break;
+ case QQmlJS::IR::BoolType:
+ out << (value ? "true" : "false");
+ 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::init(const QString *id, quint32 line, quint32 column)
+{
+ this->id = id;
+ this->builtin = builtin_invalid;
+ this->line = line;
+ this->column = column;
+}
+
+void Name::init(Builtin builtin, quint32 line, quint32 column)
+{
+ this->id = 0;
+ this->builtin = builtin;
+ 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_create_exception_handler:
+ return "builtin_create_exception_handler";
+ case Name::builtin_delete_exception_handler:
+ return "builtin_delete_exception_handler";
+ case Name::builtin_get_exception:
+ return "builtin_get_exception";
+ case IR::Name::builtin_foreach_iterator_object:
+ return "builtin_foreach_iterator_object";
+ case IR::Name::builtin_foreach_next_property_name:
+ return "builtin_foreach_next_property_name";
+ case IR::Name::builtin_push_with_scope:
+ return "builtin_push_with_scope";
+ case IR::Name::builtin_pop_scope:
+ return "builtin_pop_scope";
+ case IR::Name::builtin_declare_vars:
+ return "builtin_declare_vars";
+ case IR::Name::builtin_define_property:
+ return "builtin_define_property";
+ case IR::Name::builtin_define_array_property:
+ return "builtin_define_array_property";
+ case IR::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
+ }
+}
+
+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 << ';';
+}
+
+Function *Module::newFunction(const QString &name, Function *outer)
+{
+ Function *f = new Function(this, 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 (IR::BasicBlock *b, basicBlocks)
+ foreach (IR::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;
+}
+
+unsigned BasicBlock::newTemp()
+{
+ return function->tempCount++;
+}
+
+Temp *BasicBlock::TEMP(int index)
+{
+ Temp *e = function->New<Temp>();
+ e->init(index);
+ 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::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);
+ 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;
+}
+
+void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
+{
+ out << 'L' << index << ':' << endl;
+ foreach (Stmt *s, statements) {
+ out << '\t';
+ s->dump(out, mode);
+ out << endl;
+ }
+}
+
+} // end of namespace IR
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/v4/qv4ir_p.h b/src/v4/qv4ir_p.h
new file mode 100644
index 0000000000..4e60fa96dd
--- /dev/null
+++ b/src/v4/qv4ir_p.h
@@ -0,0 +1,724 @@
+/****************************************************************************
+**
+** 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>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QTextStream;
+class QQmlType;
+
+namespace QQmlJS {
+
+namespace VM {
+struct ExecutionContext;
+struct Value;
+}
+
+namespace IR {
+
+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;
+
+enum AluOp {
+ OpInvalid = 0,
+
+ OpIfTrue,
+ OpNot,
+ OpUMinus,
+ OpUPlus,
+ OpCompl,
+ OpIncrement,
+ OpDecrement,
+
+ OpBitAnd,
+ OpBitOr,
+ OpBitXor,
+
+ OpAdd,
+ OpSub,
+ OpMul,
+ OpDiv,
+ OpMod,
+
+ OpLShift,
+ OpRShift,
+ OpURShift,
+
+ OpGt,
+ OpLt,
+ OpGe,
+ OpLe,
+ OpEqual,
+ OpNotEqual,
+ OpStrictEqual,
+ OpStrictNotEqual,
+
+ OpInstanceof,
+ OpIn,
+
+ OpAnd,
+ OpOr,
+
+ LastAluOp = OpOr
+};
+AluOp binaryOperator(int op);
+const char *opname(IR::AluOp op);
+
+enum Type {
+ 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;
+};
+
+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_create_exception_handler,
+ builtin_delete_exception_handler,
+ builtin_get_exception,
+ 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_property,
+ builtin_define_getter_setter
+ };
+
+ const QString *id;
+ Builtin builtin;
+ 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;
+
+ void init(int index)
+ {
+ this->index = index;
+ }
+
+ 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 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 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;
+
+ int insideWith;
+
+ uint hasDirectEval: 1;
+ uint usesArgumentsObject : 1;
+ uint isStrict: 1;
+ uint unused : 29;
+
+ template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
+
+ Function(Module *module, const QString &name)
+ : module(module)
+ , pool(&module->pool)
+ , tempCount(0)
+ , maxNumberOfArguments(0)
+ , insideWith(0)
+ , hasDirectEval(false)
+ , usesArgumentsObject(false)
+ , isStrict(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);
+};
+
+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);
+
+ 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);
+
+ 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 = IR::OpInvalid);
+
+ Stmt *JUMP(BasicBlock *target);
+ Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
+ Stmt *RET(Temp *expr);
+
+ void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
+};
+
+} // end of namespace IR
+
+} // end of namespace QQmlJS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QV4IR_P_H
diff --git a/src/v4/qv4isel_llvm.cpp b/src/v4/qv4isel_llvm.cpp
new file mode 100644
index 0000000000..091936813d
--- /dev/null
+++ b/src/v4/qv4isel_llvm.cpp
@@ -0,0 +1,1388 @@
+/****************************************************************************
+**
+** 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 "qv4ir_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::callBuiltinDeleteExceptionHandler()
+{
+ // TODO
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::callBuiltinGetException(IR::Temp *result)
+{
+ // 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::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value)
+{
+ // 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::Expr *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::Expr *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::Expr *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::Expr *leftSource, IR::Expr *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::Expr *sourceExpr, 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(sourceExpr);
+ CreateCall3(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), dst, src);
+ return;
+ }
+}
+
+void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, 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(sourceExpr);
+ CreateCall4(getRuntimeFunction(opName),
+ _llvmFunction->arg_begin(), base, index, value);
+ }
+}
+
+void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *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();
+}
+
+#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_create_exception_handler:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_create_exception_handler"),
+ _llvmFunction->arg_begin(), result);
+ _llvmValue = CreateLoad(result);
+ return;
+
+ case IR::Name::builtin_delete_exception_handler:
+ CreateCall(getRuntimeFunction("__qmljs_llvm_delete_exception_handler"),
+ _llvmFunction->arg_begin());
+ return;
+
+ case IR::Name::builtin_get_exception:
+ CreateCall2(getRuntimeFunction("__qmljs_llvm_get_exception"),
+ _llvmFunction->arg_begin(), result);
+ _llvmValue = CreateLoad(result);
+ 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/v4/qv4isel_llvm_p.h b/src/v4/qv4isel_llvm_p.h
new file mode 100644
index 0000000000..46fd2fefa4
--- /dev/null
+++ b/src/v4/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 "qv4ir_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 callBuiltinDeleteExceptionHandler();
+ virtual void callBuiltinGetException(IR::Temp *result);
+ 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 callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value);
+ 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::Expr *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::Expr *source, IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target);
+ virtual void setElement(IR::Expr *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::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target);
+ virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName);
+ virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *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 *);
+
+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/v4/qv4isel_masm.cpp b/src/v4/qv4isel_masm.cpp
new file mode 100644
index 0000000000..a4a136c656
--- /dev/null
+++ b/src/v4/qv4isel_masm.cpp
@@ -0,0 +1,933 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qv4object.h"
+#include "qv4functionobject.h"
+#include "qv4regexpobject.h"
+
+#include <assembler/LinkBuffer.h>
+#include <WTFStubs.h>
+
+#include <sys/mman.h>
+#include <iostream>
+#include <cassert>
+
+#ifndef NO_UDIS86
+# include <udis86.h>
+#endif
+
+using namespace QQmlJS;
+using namespace QQmlJS::MASM;
+using namespace QQmlJS::VM;
+
+Assembler::Assembler(IR::Function* function)
+ : _function(function)
+{
+}
+
+void Assembler::registerBlock(IR::BasicBlock* block)
+{
+ _addrs[block] = label();
+}
+
+void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target)
+{
+ if (current->index + 1 != target->index)
+ _patches[target].append(jump());
+}
+
+void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump)
+{
+ _patches[targetBlock].append(targetJump);
+}
+
+Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, IR::Temp *t)
+{
+ int32_t offset = 0;
+ if (t->index < 0) {
+ const int arg = -t->index - 1;
+ loadPtr(Address(ContextRegister, offsetof(ExecutionContext, arguments)), reg);
+ offset = arg * sizeof(Value);
+ } else if (t->index < _function->locals.size()) {
+ loadPtr(Address(ContextRegister, offsetof(ExecutionContext, locals)), reg);
+ offset = t->index * sizeof(Value);
+ } else {
+ const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1;
+ // StackFrameRegister points to its old value on the stack, so even for the first temp we need to
+ // subtract at least sizeof(Value).
+ offset = - sizeof(Value) * (arg + 1);
+ reg = StackFrameRegister;
+ }
+ 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
+}
+
+void Assembler::storeValue(VM::Value value, IR::Temp* destination)
+{
+ Address addr = loadTempAddress(ScratchRegister, destination);
+ storeValue(value, addr);
+}
+
+void Assembler::enterStandardStackFrame(int locals)
+{
+#if CPU(ARM)
+ push(JSC::ARMRegisters::lr);
+#endif
+ push(StackFrameRegister);
+ move(StackPointerRegister, StackFrameRegister);
+
+ // space for the locals and the ContextRegister
+ int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*);
+
+#if CPU(X86) || CPU(X86_64)
+ frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX
+#endif
+ subPtr(TrustedImm32(frameSize), StackPointerRegister);
+
+#if CPU(X86) || CPU(ARM)
+ for (int saveReg = CalleeSavedFirstRegister; saveReg <= CalleeSavedLastRegister; ++saveReg)
+ push(static_cast<RegisterID>(saveReg));
+#endif
+ // save the ContextRegister
+ storePtr(ContextRegister, StackPointerRegister);
+}
+
+void Assembler::leaveStandardStackFrame(int locals)
+{
+ // restore the ContextRegister
+ loadPtr(StackPointerRegister, ContextRegister);
+
+#if CPU(X86) || CPU(ARM)
+ for (int saveReg = CalleeSavedLastRegister; saveReg >= CalleeSavedFirstRegister; --saveReg)
+ pop(static_cast<RegisterID>(saveReg));
+#endif
+ // space for the locals and the ContextRegister
+ int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*);
+#if CPU(X86) || CPU(X86_64)
+ frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX
+#endif
+ addPtr(TrustedImm32(frameSize), StackPointerRegister);
+
+ pop(StackFrameRegister);
+#if CPU(ARM)
+ pop(JSC::ARMRegisters::lr);
+#endif
+}
+
+
+
+#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::IR::LastAluOp + 1] = {
+ NULL_OP, // OpInvalid
+ NULL_OP, // OpIfTrue
+ NULL_OP, // OpNot
+ NULL_OP, // OpUMinus
+ NULL_OP, // OpUPlus
+ NULL_OP, // OpCompl
+ NULL_OP, // OpIncrement
+ NULL_OP, // OpDecrement
+
+ INLINE_OP(__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(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* 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(target, info.name, info.fallbackImplementation, left, right, ContextRegister);
+
+ if (binOpFinished.isSet())
+ binOpFinished.link(this);
+}
+#if OS(LINUX)
+static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions)
+{
+ QByteArray processedOutput(output);
+ for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ it != end; ++it) {
+ QByteArray ptrString = QByteArray::number(qlonglong(it.key()), 16);
+ ptrString.prepend("0x");
+ processedOutput = processedOutput.replace(ptrString, it.value());
+ }
+ fprintf(stderr, "%s\n", processedOutput.constData());
+}
+#endif
+
+void Assembler::link(VM::Function *vmFunc)
+{
+ QHashIterator<IR::BasicBlock *, QVector<Jump> > it(_patches);
+ while (it.hasNext()) {
+ it.next();
+ IR::BasicBlock *block = it.key();
+ Label target = _addrs.value(block);
+ assert(target.isSet());
+ foreach (Jump jump, it.value())
+ jump.linkTo(target, this);
+ }
+
+ JSC::JSGlobalData dummy;
+ JSC::LinkBuffer linkBuffer(dummy, this, 0);
+ QHash<void*, const char*> functions;
+ foreach (CallToLink ctl, _callsToLink) {
+ linkBuffer.link(ctl.call, ctl.externalFunction);
+ functions[ctl.externalFunction.value()] = ctl.functionName;
+ }
+
+ 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();
+ vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+
+ WTF::setDataFile(stderr);
+#if OS(LINUX)
+ fclose(disasmStream);
+#if CPU(X86) || CPU(X86_64)
+ printDisassembledOutputWithCalls(disasmOutput, functions);
+#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, IR::Module *module)
+ : EvalInstructionSelection(engine, module)
+ , _block(0)
+ , _function(0)
+ , _vmFunction(0)
+ , _asm(0)
+{
+}
+
+InstructionSelection::~InstructionSelection()
+{
+ delete _asm;
+}
+
+void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function)
+{
+ qSwap(_function, function);
+ qSwap(_vmFunction, vmFunction);
+ Assembler* oldAssembler = _asm;
+ _asm = new Assembler(_function);
+
+ int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1;
+ locals = (locals + 1) & ~1;
+ _asm->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
+#if CPU(X86)
+ _asm->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister);
+#elif CPU(X86_64) || CPU(ARM)
+ _asm->move(_asm->registerForArgument(contextPointer), Assembler::ContextRegister);
+#else
+ assert(!"TODO");
+#endif
+
+ foreach (IR::BasicBlock *block, _function->basicBlocks) {
+ _block = block;
+ _asm->registerBlock(_block);
+ foreach (IR::Stmt *s, block->statements) {
+ s->accept(this);
+ }
+ }
+
+ _asm->leaveStandardStackFrame(locals);
+#ifndef VALUE_FITS_IN_REGISTER
+ // Emulate ret(n) instruction
+ // Pop off return address into scratch register ...
+ _asm->pop(Assembler::ScratchRegister);
+ // ... and overwrite the invisible argument with
+ // the return address.
+ _asm->poke(Assembler::ScratchRegister);
+#endif
+ _asm->ret();
+
+ _asm->link(_vmFunction);
+
+ qSwap(_vmFunction, vmFunction);
+ qSwap(_function, function);
+ delete _asm;
+ _asm = oldAssembler;
+}
+
+void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result)
+{
+ callRuntimeMethod(result, __qmljs_call_activation_property, func, args);
+}
+
+void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_typeof_member, base, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_typeof_element, base, index, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_typeof_name, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_typeof, value, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister, base, identifier(name));
+}
+
+void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister, base, index);
+}
+
+void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister, identifier(name));
+}
+
+void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result)
+{
+ _asm->storeValue(Value::fromBoolean(false), result);
+}
+
+void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_increment_member, base, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_increment_element, base, index, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_increment_name, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_increment, Assembler::PointerToValue(value), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_decrement_member, base, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_decrement_element, base, index, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_decrement_name, identifier(name), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_builtin_post_decrement, Assembler::PointerToValue(value), Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinThrow(IR::Temp *arg)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, arg, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result)
+{
+ generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_create_exception_handler, Assembler::ContextRegister);
+ generateFunctionCall(Assembler::ReturnValueRegister, setjmp, Assembler::ReturnValueRegister);
+ Address addr = _asm->loadTempAddress(Assembler::ScratchRegister, result);
+ _asm->store32(Assembler::ReturnValueRegister, addr);
+ addr.offset += 4;
+ _asm->store32(Assembler::TrustedImm32(Value::Boolean_Type), addr);
+}
+
+void InstructionSelection::callBuiltinDeleteExceptionHandler()
+{
+ generateFunctionCall(Assembler::Void, __qmljs_delete_exception_handler, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinGetException(IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_get_exception, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_foreach_iterator_object, arg, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result)
+{
+ generateFunctionCall(result, __qmljs_foreach_next_property_name, arg);
+}
+
+void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg)
+{
+ generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, 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(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter,
+ object, identifier(name), getter, setter, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property,
+ object, identifier(name), value, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array_property,
+ object, Assembler::TrustedImm32(index), value, Assembler::ContextRegister);
+}
+
+void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ IR::Temp* thisObject = 0;
+ generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister, thisObject, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::loadThisObject(IR::Temp *temp)
+{
+ generateFunctionCall(temp, __qmljs_get_thisObject, Assembler::ContextRegister);
+}
+
+void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp)
+{
+ _asm->storeValue(convertToValue(sourceConst), targetTemp);
+}
+
+void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp)
+{
+ Value v = Value::fromString(identifier(str));
+ _asm->storeValue(v, targetTemp);
+}
+
+void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp)
+{
+ Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value,
+ sourceRegexp->flags));
+ _vmFunction->generatedValues.append(v);
+ _asm->storeValue(v, targetTemp);
+}
+
+void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp)
+{
+ String *propertyName = identifier(name);
+ generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, propertyName);
+}
+
+void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
+{
+ String *propertyName = identifier(targetName);
+ generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, Assembler::ContextRegister, propertyName, source);
+}
+
+void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target)
+{
+ VM::Function *vmFunc = vmFunction(closure->value);
+ assert(vmFunc);
+ generateFunctionCall(target, __qmljs_init_closure, Assembler::TrustedImmPtr(vmFunc), Assembler::ContextRegister);
+}
+
+void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target)
+{
+ generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister, base, identifier(name));
+}
+
+void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, targetBase, identifier(targetName), source);
+}
+
+void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target)
+{
+ generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister, base, index);
+}
+
+void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex)
+{
+ generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, targetBase, targetIndex, source);
+}
+
+void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ _asm->copyValue(targetTemp, sourceTemp);
+}
+
+#define setOp(op, opName, operation) \
+ do { op = operation; opName = isel_stringIfy(operation); } while (0)
+
+void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp)
+{
+ Value (*op)(const Value value, ExecutionContext *ctx) = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpIfTrue: assert(!"unreachable"); break;
+ case IR::OpNot: setOp(op, opName, __qmljs_not); break;
+ case IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break;
+ case IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break;
+ case IR::OpCompl: setOp(op, opName, __qmljs_compl); break;
+ case IR::OpIncrement: setOp(op, opName, __qmljs_increment); break;
+ case IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break;
+ default: assert(!"unreachable"); break;
+ } // switch
+
+ if (op)
+ _asm->generateFunctionCallImp(targetTemp, opName, op, sourceTemp,
+ Assembler::ContextRegister);
+}
+
+void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target)
+{
+ _asm->generateBinOp(oper, target, leftSource, rightSource);
+}
+
+void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName)
+{
+ void (*op)(const Value value, String *name, ExecutionContext *ctx) = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break;
+ case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break;
+ case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break;
+ case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break;
+ case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break;
+ case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break;
+ case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break;
+ case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break;
+ case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break;
+ case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break;
+ case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ if (op) {
+ _asm->generateFunctionCallImp(Assembler::Void, opName, op, sourceExpr, identifier(targetName), Assembler::ContextRegister);
+ }
+}
+
+void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp)
+{
+ void (*op)(Value base, Value index, Value value, ExecutionContext *ctx) = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break;
+ case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break;
+ case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break;
+ case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break;
+ case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break;
+ case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break;
+ case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break;
+ case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break;
+ case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break;
+ case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break;
+ case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (op) {
+ _asm->generateFunctionCallImp(Assembler::Void, opName, op, targetBaseTemp, targetIndexTemp, sourceExpr, Assembler::ContextRegister);
+ }
+}
+
+void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName)
+{
+ void (*op)(Value value, Value base, String *name, ExecutionContext *ctx) = 0;
+ const char *opName = 0;
+ switch (oper) {
+ case IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break;
+ case IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break;
+ case IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break;
+ case IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break;
+ case IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break;
+ case IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break;
+ case IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break;
+ case IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break;
+ case IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break;
+ case IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break;
+ case IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (op) {
+ String* member = identifier(targetName);
+ _asm->generateFunctionCallImp(Assembler::Void, opName, op, source, targetBase, member, Assembler::ContextRegister);
+ }
+}
+
+void InstructionSelection::callProperty(IR::Temp *base, const QString &name,
+ IR::ExprList *args, IR::Temp *result)
+{
+ assert(base != 0);
+
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(result, __qmljs_call_property,
+ Assembler::ContextRegister, base, identifier(name),
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result)
+{
+ assert(base != 0);
+
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(result, __qmljs_call_element,
+ Assembler::ContextRegister, base, index,
+ baseAddressForCallArguments(),
+ Assembler::TrustedImm32(argc));
+}
+
+String *InstructionSelection::identifier(const QString &s)
+{
+ String *str = engine()->identifier(s);
+ _vmFunction->identifiers.append(str);
+ return str;
+}
+
+void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result)
+{
+ assert(func != 0);
+
+ callRuntimeMethod(result, __qmljs_construct_activation_property, func, args);
+}
+
+void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result)
+{
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, base, identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result)
+{
+ assert(value != 0);
+
+ int argc = prepareVariableArguments(args);
+ generateFunctionCall(result, __qmljs_construct_value, Assembler::ContextRegister, value, baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::visitJump(IR::Jump *s)
+{
+ _asm->jumpToBlock(_block, s->target);
+}
+
+void InstructionSelection::visitCJump(IR::CJump *s)
+{
+ if (IR::Temp *t = s->cond->asTemp()) {
+ Address temp = _asm->loadTempAddress(Assembler::ScratchRegister, t);
+ Address tag = temp;
+ tag.offset += offsetof(VM::Value, tag);
+ Assembler::Jump booleanConversion = _asm->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type));
+
+ Address data = temp;
+ data.offset += offsetof(VM::Value, int_32);
+ _asm->load32(data, Assembler::ReturnValueRegister);
+ Assembler::Jump testBoolean = _asm->jump();
+
+ booleanConversion.link(_asm);
+ {
+ generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, t, Assembler::ContextRegister);
+ }
+
+ testBoolean.link(_asm);
+ Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
+ _asm->addPatch(s->iftrue, target);
+
+ _asm->jumpToBlock(_block, s->iffalse);
+ return;
+ } else if (IR::Binop *b = s->cond->asBinop()) {
+ if ((b->left->asTemp() || b->left->asConst()) &&
+ (b->right->asTemp() || b->right->asConst())) {
+ Bool (*op)(const Value, const Value, ExecutionContext *ctx) = 0;
+ const char *opName = 0;
+ switch (b->op) {
+ default: Q_UNREACHABLE(); assert(!"todo"); break;
+ case IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break;
+ case IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break;
+ case IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break;
+ case IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break;
+ case IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break;
+ case IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break;
+ case IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break;
+ case IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break;
+ case IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break;
+ case IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break;
+ } // switch
+
+ _asm->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, b->left, b->right, Assembler::ContextRegister);
+
+ Assembler::Jump target = _asm->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
+ _asm->addPatch(s->iftrue, target);
+
+ _asm->jumpToBlock(_block, s->iffalse);
+ return;
+ } else {
+ assert(!"wip");
+ }
+ Q_UNIMPLEMENTED();
+ }
+ Q_UNIMPLEMENTED();
+ assert(!"TODO");
+}
+
+void InstructionSelection::visitRet(IR::Ret *s)
+{
+ if (IR::Temp *t = s->expr->asTemp()) {
+#ifdef VALUE_FITS_IN_REGISTER
+ _asm->copyValue(Assembler::ReturnValueRegister, t);
+#else
+ _asm->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister);
+ _asm->copyValue(Address(Assembler::ReturnValueRegister, 0), t);
+#endif
+ return;
+ }
+ Q_UNIMPLEMENTED();
+ Q_UNUSED(s);
+}
+
+int InstructionSelection::prepareVariableArguments(IR::ExprList* args)
+{
+ int argc = 0;
+ for (IR::ExprList *it = args; it; it = it->next) {
+ ++argc;
+ }
+
+ int i = 0;
+ for (IR::ExprList *it = args; it; it = it->next, ++i) {
+ IR::Temp *arg = it->expr->asTemp();
+ assert(arg != 0);
+ _asm->copyValue(argumentAddressForCall(i), arg);
+ }
+
+ return argc;
+}
+
+void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args)
+{
+ IR::Name *baseName = base->asName();
+ assert(baseName != 0);
+
+ int argc = prepareVariableArguments(args);
+ _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, identifier(*baseName->id), baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+void InstructionSelection::callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args)
+{
+ int argc = prepareVariableArguments(args);
+ _asm->generateFunctionCallImp(result, name, method, Assembler::ContextRegister, baseAddressForCallArguments(), Assembler::TrustedImm32(argc));
+}
+
+
diff --git a/src/v4/qv4isel_masm_p.h b/src/v4/qv4isel_masm_p.h
new file mode 100644
index 0000000000..de0971f0cb
--- /dev/null
+++ b/src/v4/qv4isel_masm_p.h
@@ -0,0 +1,798 @@
+/****************************************************************************
+**
+** 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 "qv4ir_p.h"
+#include "qv4isel_p.h"
+#include "qv4isel_util_p.h"
+#include "qv4object.h"
+#include "qmljs_runtime.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(IR::Function* function);
+#if CPU(X86)
+
+#undef VALUE_FITS_IN_REGISTER
+
+ static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
+ static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
+ static const RegisterID ContextRegister = JSC::X86Registers::esi;
+ static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
+ static const RegisterID ScratchRegister = JSC::X86Registers::ecx;
+ static const RegisterID CalleeSavedFirstRegister = ScratchRegister;
+ static const RegisterID CalleeSavedLastRegister = ScratchRegister;
+ 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;
+ }
+#elif CPU(X86_64)
+
+#define VALUE_FITS_IN_REGISTER
+
+ static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
+ static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
+ 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];
+ };
+#elif CPU(ARM)
+
+#undef VALUE_FITS_IN_REGISTER
+
+ static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4;
+ static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp;
+ static const RegisterID ContextRegister = JSC::ARMRegisters::r5;
+ static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0;
+ static const RegisterID ScratchRegister = JSC::ARMRegisters::r6;
+ static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4;
+ static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11;
+ static const RegisterID IntegerOpRegister = JSC::X86Registers::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);
+ };
+#else
+#error Argh.
+#endif
+
+ // Explicit type to allow distinguishing between
+ // pushing an address itself or the value it points
+ // to onto the stack when calling functions.
+ struct Pointer : public Address
+ {
+ explicit Pointer(const Address& addr)
+ : Address(addr)
+ {}
+ explicit Pointer(RegisterID reg, int32_t offset)
+ : Address(reg, offset)
+ {}
+ };
+
+ struct VoidType {};
+ static const VoidType Void;
+
+
+ typedef JSC::FunctionPtr FunctionPtr;
+
+ struct CallToLink {
+ Call call;
+ FunctionPtr externalFunction;
+ const char* functionName;
+ };
+ struct PointerToValue {
+ PointerToValue(IR::Temp *value) : value(value) {}
+ IR::Temp *value;
+ };
+
+ void callAbsolute(const char* functionName, FunctionPtr function) {
+ CallToLink ctl;
+ ctl.call = call();
+ ctl.externalFunction = function;
+ ctl.functionName = functionName;
+ _callsToLink.append(ctl);
+ }
+
+ void registerBlock(IR::BasicBlock*);
+ void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target);
+ void addPatch(IR::BasicBlock* targetBlock, Jump targetJump);
+
+ Pointer loadTempAddress(RegisterID reg, IR::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)
+ {
+ assert(temp.value);
+
+ Pointer addr = loadTempAddress(dest, temp.value);
+ loadArgument(addr, dest);
+ }
+
+#ifdef VALUE_FITS_IN_REGISTER
+ void loadArgument(IR::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(IR::Const* c, RegisterID dest)
+ {
+ VM::Value v = convertToValue(c);
+ move(TrustedImm64(v.val), dest);
+ }
+
+ void loadArgument(IR::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(IR::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, IR::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)
+ {
+ assert (temp.value);
+
+ Pointer ptr = loadTempAddress(ScratchRegister, temp.value);
+ push(ptr);
+ }
+
+ void push(IR::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(IR::Const* c)
+ {
+ VM::Value v = convertToValue(c);
+ push(v);
+ }
+
+ void push(IR::Expr* e)
+ {
+ if (!e) {
+ VM::Value undefined = VM::Value::undefinedValue();
+ push(undefined);
+ } else if (IR::Const *c = e->asConst())
+ push(c);
+ else if (IR::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(IR::Temp* temp, FPRegisterID dest)
+ {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp);
+ loadDouble(ptr, dest);
+ }
+
+ using JSC::MacroAssembler::storeDouble;
+ void storeDouble(FPRegisterID source, IR::Temp* temp)
+ {
+ Pointer ptr = loadTempAddress(ScratchRegister, temp);
+ storeDouble(source, ptr);
+ }
+
+ template <typename Result, typename Source>
+ void copyValue(Result result, Source 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, IR::Temp* temp);
+
+ void enterStandardStackFrame(int locals);
+ void leaveStandardStackFrame(int locals);
+
+ void callFunctionPrologue()
+ {
+#if CPU(X86)
+ // Callee might clobber it :(
+ push(ContextRegister);
+#endif
+ }
+ void callFunctionEpilogue()
+ {
+#if CPU(X86)
+ pop(ContextRegister);
+#endif
+ }
+
+ static inline int sizeOfArgument(VoidType)
+ { return 0; }
+ static inline int sizeOfArgument(RegisterID)
+ { return RegisterSize; }
+ static inline int sizeOfArgument(IR::Temp*)
+ { return 8; } // Size of value
+ static inline int sizeOfArgument(IR::Expr*)
+ { return 8; } // Size of value
+ static inline int sizeOfArgument(const Pointer&)
+ { return sizeof(void*); }
+ static inline int sizeOfArgument(VM::String* string)
+ { return sizeof(string); }
+ static inline int sizeOfArgument(const PointerToValue &)
+ { return sizeof(void *); }
+ static inline int sizeOfArgument(TrustedImmPtr)
+ { return sizeof(void*); }
+ static inline int sizeOfArgument(TrustedImm32)
+ { return 4; }
+
+ struct ArgumentLoader
+ {
+ ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments)
+ : assembler(_assembler)
+ , stackSpaceForArguments(0)
+ , currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1))
+ {
+ }
+
+ template <typename T>
+ void load(T argument)
+ {
+ if (currentRegisterIndex >= 0) {
+ assembler->loadArgument(argument, registerForArgument(currentRegisterIndex));
+ --currentRegisterIndex;
+ } else {
+ assembler->push(argument);
+ stackSpaceForArguments += sizeOfArgument(argument);
+ }
+ }
+
+ void load(VoidType)
+ {
+ if (currentRegisterIndex >= 0)
+ --currentRegisterIndex;
+ }
+
+ Assembler *assembler;
+ int stackSpaceForArguments;
+ int currentRegisterIndex;
+ };
+
+ 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)
+ {
+ callFunctionPrologue();
+
+ int totalNumberOfArgs = 5;
+
+ // 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;
+ }
+
+ ArgumentLoader l(this, totalNumberOfArgs);
+ l.load(arg5);
+ l.load(arg4);
+ l.load(arg3);
+ l.load(arg2);
+ l.load(arg1);
+
+ if (returnValueOnStack) {
+ // Load address of return value
+ l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments));
+ }
+
+ callAbsolute(functionName, function);
+
+ int stackSizeToCorrect = l.stackSpaceForArguments;
+ if (returnValueOnStack) {
+ stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value)
+ stackSizeToCorrect += sizeOfReturnValueOnStack;
+ }
+
+ storeArgument(ReturnValueRegister, r);
+
+ if (stackSizeToCorrect)
+ add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister);
+
+ callFunctionEpilogue();
+ }
+
+ 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::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *);
+ MemRegBinOp inlineMemRegOp;
+ ImmRegBinOp inlineImmRegOp;
+ };
+
+ static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1];
+
+ void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right);
+
+ Jump inline_add32(Address addr, RegisterID reg)
+ {
+ return branchAdd32(Overflow, addr, reg);
+ }
+
+ Jump inline_add32(TrustedImm32 imm, RegisterID reg)
+ {
+ return branchAdd32(Overflow, imm, reg);
+ }
+
+ Jump inline_sub32(Address addr, RegisterID reg)
+ {
+ return branchSub32(Overflow, addr, reg);
+ }
+
+ Jump inline_sub32(TrustedImm32 imm, RegisterID reg)
+ {
+ return branchSub32(Overflow, imm, reg);
+ }
+
+ Jump inline_mul32(Address addr, RegisterID reg)
+ {
+ return branchMul32(Overflow, addr, reg);
+ }
+
+ 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 Jump();
+ }
+
+ Jump inline_ushr32(TrustedImm32 imm, RegisterID reg)
+ {
+ imm.m_value &= 0x1f;
+ urshift32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_and32(Address addr, RegisterID reg)
+ {
+ and32(addr, reg);
+ return Jump();
+ }
+
+ Jump inline_and32(TrustedImm32 imm, RegisterID reg)
+ {
+ and32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_or32(Address addr, RegisterID reg)
+ {
+ or32(addr, reg);
+ return Jump();
+ }
+
+ Jump inline_or32(TrustedImm32 imm, RegisterID reg)
+ {
+ or32(imm, reg);
+ return Jump();
+ }
+
+ Jump inline_xor32(Address addr, RegisterID reg)
+ {
+ xor32(addr, reg);
+ return Jump();
+ }
+
+ Jump inline_xor32(TrustedImm32 imm, RegisterID reg)
+ {
+ xor32(imm, reg);
+ return Jump();
+ }
+
+ void link(VM::Function *vmFunc);
+
+private:
+ IR::Function* _function;
+ QHash<IR::BasicBlock *, Label> _addrs;
+ QHash<IR::BasicBlock *, QVector<Jump> > _patches;
+ QList<CallToLink> _callsToLink;
+};
+
+class Q_V4_EXPORT InstructionSelection:
+ protected IR::InstructionSelection,
+ public EvalInstructionSelection
+{
+public:
+ InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module);
+ ~InstructionSelection();
+
+ virtual void run(VM::Function *vmFunction, IR::Function *function);
+
+protected:
+ 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 callBuiltinDeleteExceptionHandler();
+ virtual void callBuiltinGetException(IR::Temp *result);
+ 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 callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value);
+ 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 callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result);
+ virtual void loadThisObject(IR::Temp *temp);
+ virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp);
+ 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::Expr *source, const QString &targetName);
+ virtual void initClosure(IR::Closure *closure, IR::Temp *target);
+ virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target);
+ virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName);
+ virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target);
+ virtual void setElement(IR::Expr *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::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target);
+ virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName);
+ virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp);
+ virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName);
+
+ typedef Assembler::Address Address;
+ typedef Assembler::Pointer Pointer;
+
+ Address addressForArgument(int index) const
+ {
+ if (index < Assembler::RegisterArgumentCount)
+ return Address(_asm->registerForArgument(index), 0);
+
+ // 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 - Assembler::RegisterArgumentCount + 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::StackFrameRegister, sizeof(VM::Value) * (-index)
+ - sizeof(void*) // size of ebp
+ );
+ }
+ Pointer baseAddressForCallArguments()
+ {
+ return argumentAddressForCall(0);
+ }
+
+ VM::String *identifier(const QString &s);
+ 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 visitJump(IR::Jump *);
+ virtual void visitCJump(IR::CJump *);
+ virtual void visitRet(IR::Ret *);
+
+private:
+ #define isel_stringIfyx(s) #s
+ #define isel_stringIfy(s) isel_stringIfyx(s)
+
+ #define generateFunctionCall(t, function, ...) \
+ _asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__)
+
+ int prepareVariableArguments(IR::ExprList* args);
+
+ typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc);
+ typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc);
+ void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args);
+ void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args);
+#define callRuntimeMethod(result, function, ...) \
+ callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__)
+
+ IR::BasicBlock *_block;
+ IR::Function* _function;
+ VM::Function* _vmFunction;
+ Assembler* _asm;
+};
+
+class Q_V4_EXPORT ISelFactory: public EvalISelFactory
+{
+public:
+ virtual ~ISelFactory() {}
+ virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module)
+ { return new InstructionSelection(engine, module); }
+};
+
+} // end of namespace MASM
+} // end of namespace QQmlJS
+
+#endif // QV4ISEL_MASM_P_H
diff --git a/src/v4/qv4isel_p.cpp b/src/v4/qv4isel_p.cpp
new file mode 100644
index 0000000000..b408be3673
--- /dev/null
+++ b/src/v4/qv4isel_p.cpp
@@ -0,0 +1,414 @@
+#include "debugging.h"
+#include "qmljs_engine.h"
+#include "qv4ir_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::IR;
+
+EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module)
+ : _engine(engine)
+{
+ assert(engine);
+ assert(module);
+
+ createFunctionMapping(engine, module->rootFunction);
+ foreach (IR::Function *f, module->functions) {
+ assert(_irToVM.contains(f));
+ }
+}
+
+EvalInstructionSelection::~EvalInstructionSelection()
+{}
+
+EvalISelFactory::~EvalISelFactory()
+{}
+
+VM::Function *EvalInstructionSelection::createFunctionMapping(VM::ExecutionEngine *engine, 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;
+
+ foreach (const QString *formal, irFunction->formals)
+ if (formal)
+ vmFunction->formals.append(engine->identifier(*formal));
+ foreach (const QString *local, irFunction->locals)
+ if (local)
+ vmFunction->locals.append(engine->identifier(*local));
+
+ foreach (IR::Function *function, irFunction->nestedFunctions)
+ createFunctionMapping(engine, 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(IR::Move *s)
+{
+ if (s->op == IR::OpInvalid) {
+ if (IR::Name *n = s->target->asName()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ setActivationProperty(s->source, *n->id);
+ return;
+ }
+ } else if (IR::Temp *t = s->target->asTemp()) {
+ if (IR::Name *n = s->source->asName()) {
+ if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin.
+ loadThisObject(t);
+ else
+ getActivationProperty(*n->id, t);
+ return;
+ } else if (IR::Const *c = s->source->asConst()) {
+ loadConst(c, t);
+ return;
+ } else if (IR::Temp *t2 = s->source->asTemp()) {
+ copyValue(t2, t);
+ return;
+ } else if (IR::String *str = s->source->asString()) {
+ loadString(*str->value, t);
+ return;
+ } else if (IR::RegExp *re = s->source->asRegExp()) {
+ loadRegexp(re, t);
+ return;
+ } else if (IR::Closure *clos = s->source->asClosure()) {
+ initClosure(clos, t);
+ return;
+ } else if (IR::New *ctor = s->source->asNew()) {
+ if (Name *func = ctor->base->asName()) {
+ constructActivationProperty(func, ctor->args, t);
+ return;
+ } else if (IR::Member *member = ctor->base->asMember()) {
+ constructProperty(member->base->asTemp(), *member->name, ctor->args, t);
+ return;
+ } else if (IR::Temp *value = ctor->base->asTemp()) {
+ constructValue(value, ctor->args, t);
+ return;
+ }
+ } else if (IR::Member *m = s->source->asMember()) {
+ if (IR::Temp *base = m->base->asTemp()) {
+ getProperty(base, *m->name, t);
+ return;
+ }
+ } else if (IR::Subscript *ss = s->source->asSubscript()) {
+ getElement(ss->base->asTemp(), ss->index->asTemp(), t);
+ return;
+ } else if (IR::Unop *u = s->source->asUnop()) {
+ if (IR::Temp *e = u->expr->asTemp()) {
+ unop(u->op, e, t);
+ return;
+ }
+ } else if (IR::Binop *b = s->source->asBinop()) {
+ if ((b->left->asTemp() || b->left->asConst())
+ && (b->right->asTemp() || b->right->asConst())) {
+ binop(b->op, b->left, b->right, t);
+ return;
+ }
+ } else if (IR::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 (IR::Temp *value = c->base->asTemp()) {
+ callValue(value, c->args, t);
+ return;
+ }
+ }
+ } else if (IR::Member *m = s->target->asMember()) {
+ if (IR::Temp *base = m->base->asTemp()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ setProperty(s->source, base, *m->name);
+ return;
+ }
+ }
+ } else if (IR::Subscript *ss = s->target->asSubscript()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ setElement(s->source, ss->base->asTemp(), ss->index->asTemp());
+ return;
+ }
+ }
+ } else {
+ // inplace assignment, e.g. x += 1, ++x, ...
+ if (IR::Temp *t = s->target->asTemp()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ binop(s->op, t, s->source, t);
+ return;
+ }
+ } else if (IR::Name *n = s->target->asName()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ inplaceNameOp(s->op, s->source, *n->id);
+ return;
+ }
+ } else if (IR::Subscript *ss = s->target->asSubscript()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ inplaceElementOp(s->op, s->source, ss->base->asTemp(),
+ ss->index->asTemp());
+ return;
+ }
+ } else if (IR::Member *m = s->target->asMember()) {
+ if (s->source->asTemp() || s->source->asConst()) {
+ inplaceMemberOp(s->op, s->source, m->base->asTemp(), *m->name);
+ return;
+ }
+ }
+ }
+
+ // For anything else...:
+ Q_UNIMPLEMENTED();
+ s->dump(qout, IR::Stmt::MIR);
+ qout << endl;
+ assert(!"TODO");
+}
+
+InstructionSelection::~InstructionSelection()
+{
+}
+
+void InstructionSelection::visitEnter(Enter *)
+{
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::visitLeave(Leave *)
+{
+ Q_UNREACHABLE();
+}
+
+void InstructionSelection::visitExp(IR::Exp *s)
+{
+ if (IR::Call *c = s->expr->asCall()) {
+ // These are calls where the result is ignored.
+ if (c->base->asName()) {
+ callBuiltin(c, 0);
+ } else if (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(IR::Call *call, IR::Temp *result)
+{
+ IR::Name *baseName = call->base->asName();
+ assert(baseName != 0);
+
+ switch (baseName->builtin) {
+ case IR::Name::builtin_invalid:
+ callBuiltinInvalid(baseName, call->args, result);
+ return;
+
+ case IR::Name::builtin_typeof: {
+ if (IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinTypeofMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (IR::Name *n = call->args->expr->asName()) {
+ callBuiltinTypeofName(*n->id, result);
+ return;
+ } else if (IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinTypeofValue(arg, result);
+ return;
+ }
+ } break;
+
+ case IR::Name::builtin_delete: {
+ if (IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinDeleteMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (IR::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 IR::Name::builtin_postincrement: {
+ if (IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (IR::Name *n = call->args->expr->asName()) {
+ callBuiltinPostIncrementName(*n->id, result);
+ return;
+ } else if (IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinPostIncrementValue(arg, result);
+ return;
+ }
+ } break;
+
+ case IR::Name::builtin_postdecrement: {
+ if (IR::Member *m = call->args->expr->asMember()) {
+ callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result);
+ return;
+ } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
+ callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result);
+ return;
+ } else if (IR::Name *n = call->args->expr->asName()) {
+ callBuiltinPostDecrementName(*n->id, result);
+ return;
+ } else if (IR::Temp *arg = call->args->expr->asTemp()){
+ assert(arg != 0);
+ callBuiltinPostDecrementValue(arg, result);
+ return;
+ }
+ } break;
+
+ case IR::Name::builtin_throw: {
+ IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinThrow(arg);
+ } return;
+
+ case IR::Name::builtin_create_exception_handler:
+ callBuiltinCreateExceptionHandler(result);
+ return;
+
+ case IR::Name::builtin_delete_exception_handler:
+ callBuiltinDeleteExceptionHandler();
+ return;
+
+ case IR::Name::builtin_get_exception:
+ callBuiltinGetException(result);
+ return;
+
+ case IR::Name::builtin_foreach_iterator_object: {
+ IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinForeachIteratorObject(arg, result);
+ } return;
+
+ case IR::Name::builtin_foreach_next_property_name: {
+ IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinForeachNextPropertyname(arg, result);
+ } return;
+ case IR::Name::builtin_push_with_scope: {
+ IR::Temp *arg = call->args->expr->asTemp();
+ assert(arg != 0);
+ callBuiltinPushWithScope(arg);
+ } return;
+
+ case IR::Name::builtin_pop_scope:
+ callBuiltinPopScope();
+ return;
+
+ case IR::Name::builtin_declare_vars: {
+ if (!call->args)
+ return;
+ IR::Const *deletable = call->args->expr->asConst();
+ assert(deletable->type == IR::BoolType);
+ for (IR::ExprList *it = call->args->next; it; it = it->next) {
+ IR::Name *arg = it->expr->asName();
+ assert(arg != 0);
+ callBuiltinDeclareVar(deletable->value != 0, *arg->id);
+ }
+ } return;
+
+ case IR::Name::builtin_define_getter_setter: {
+ if (!call->args)
+ return;
+ IR::ExprList *args = call->args;
+ IR::Temp *object = args->expr->asTemp();
+ assert(object);
+ args = args->next;
+ assert(args);
+ IR::Name *name = args->expr->asName();
+ args = args->next;
+ assert(args);
+ IR::Temp *getter = args->expr->asTemp();
+ args = args->next;
+ assert(args);
+ IR::Temp *setter = args->expr->asTemp();
+
+ callBuiltinDefineGetterSetter(object, *name->id, getter, setter);
+ } return;
+
+ case IR::Name::builtin_define_property: {
+ if (!call->args)
+ return;
+ IR::ExprList *args = call->args;
+ IR::Temp *object = args->expr->asTemp();
+ assert(object);
+ args = args->next;
+ assert(args);
+ IR::Name *name = args->expr->asName();
+ args = args->next;
+ assert(args);
+ IR::Temp *value = args->expr->asTemp();
+
+ callBuiltinDefineProperty(object, *name->id, value);
+ } return;
+
+ case IR::Name::builtin_define_array_property: {
+ if (!call->args)
+ return;
+ IR::ExprList *args = call->args;
+ IR::Temp *object = args->expr->asTemp();
+ assert(object);
+ args = args->next;
+ assert(args);
+ IR::Const *index = args->expr->asConst();
+ args = args->next;
+ assert(args);
+ IR::Temp *value = args->expr->asTemp();
+
+ callBuiltinDefineArrayProperty(object, int(index->value), value);
+ } return;
+
+ default:
+ break;
+ }
+
+ Q_UNIMPLEMENTED();
+ call->dump(qout); qout << endl;
+ assert(!"TODO!");
+ Q_UNREACHABLE();
+}
diff --git a/src/v4/qv4isel_p.h b/src/v4/qv4isel_p.h
new file mode 100644
index 0000000000..d4f7052373
--- /dev/null
+++ b/src/v4/qv4isel_p.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 Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef QV4ISEL_P_H
+#define QV4ISEL_P_H
+
+#include "qv4global.h"
+#include "qv4ir_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, IR::Module *module);
+ virtual ~EvalInstructionSelection() = 0;
+
+ VM::Function *vmFunction(IR::Function *f);
+
+protected:
+ VM::Function *createFunctionMapping(VM::ExecutionEngine *engine, IR::Function *irFunction);
+ VM::ExecutionEngine *engine() const { return _engine; }
+ virtual void run(VM::Function *vmFunction, IR::Function *function) = 0;
+
+private:
+ VM::ExecutionEngine *_engine;
+ QHash<IR::Function *, VM::Function *> _irToVM;
+};
+
+class Q_V4_EXPORT EvalISelFactory
+{
+public:
+ virtual ~EvalISelFactory() = 0;
+ virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) = 0;
+};
+
+namespace IR {
+class Q_V4_EXPORT InstructionSelection: protected IR::StmtVisitor
+{
+public:
+ virtual ~InstructionSelection() = 0;
+
+public: // visitor methods for StmtVisitor:
+ virtual void visitMove(IR::Move *s);
+ virtual void visitEnter(IR::Enter *);
+ virtual void visitLeave(IR::Leave *);
+ virtual void visitExp(IR::Exp *s);
+
+public: // to implement by subclasses:
+ virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteValue(IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result) = 0;
+ virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) = 0;
+ virtual void callBuiltinThrow(IR::Temp *arg) = 0;
+ virtual void callBuiltinCreateExceptionHandler(IR::Temp *result) = 0;
+ virtual void callBuiltinDeleteExceptionHandler() = 0;
+ virtual void callBuiltinGetException(IR::Temp *result) = 0;
+ virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) = 0;
+ virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) = 0;
+ virtual void callBuiltinPushWithScope(IR::Temp *arg) = 0;
+ virtual void callBuiltinPopScope() = 0;
+ virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0;
+ virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) = 0;
+ virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) = 0;
+ virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) = 0;
+ virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) = 0;
+ virtual void loadThisObject(IR::Temp *temp) = 0;
+ virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) = 0;
+ virtual void loadString(const QString &str, IR::Temp *targetTemp) = 0;
+ virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) = 0;
+ virtual void getActivationProperty(const QString &name, IR::Temp *temp) = 0;
+ virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0;
+ virtual void initClosure(IR::Closure *closure, IR::Temp *target) = 0;
+ virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target) = 0;
+ virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0;
+ virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) = 0;
+ virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) = 0;
+ virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0;
+ virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) = 0;
+ virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) = 0;
+ virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) = 0;
+ virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) = 0;
+ virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) = 0;
+
+private:
+ void callBuiltin(IR::Call *c, IR::Temp *temp);
+};
+} // namespace IR
+
+} // namespace QQmlJS
+
+#endif // QV4ISEL_P_H
diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h
new file mode 100644
index 0000000000..693af03e88
--- /dev/null
+++ b/src/v4/qv4isel_util_p.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef QV4ISEL_UTIL_P_H
+#define QV4ISEL_UTIL_P_H
+
+#include "qmljs_runtime.h"
+#include "qv4ir_p.h"
+
+namespace QQmlJS {
+
+inline VM::Value convertToValue(IR::Const *c)
+{
+ switch (c->type) {
+ case IR::NullType:
+ return VM::Value::nullValue();
+ case IR::UndefinedType:
+ return VM::Value::undefinedValue();
+ case IR::BoolType:
+ return VM::Value::fromBoolean(c->value != 0);
+ case IR::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) {
+ 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/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp
new file mode 100644
index 0000000000..fa39476f4e
--- /dev/null
+++ b/src/v4/qv4jsonobject.cpp
@@ -0,0 +1,935 @@
+/****************************************************************************
+**
+** 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>
+
+
+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";
+ if (!o->members)
+ o->members.reset(new PropertyTable());
+
+ 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;
+
+ PropertyDescriptor *p = o->members->insert(context->engine->identifier(key));
+ p->value = val;
+
+ END;
+ return true;
+}
+
+/*
+ array = begin-array [ value *( value-separator value ) ] end-array
+*/
+Value Parser::parseArray()
+{
+ BEGIN << "parseArray";
+ Array array;
+
+ 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.set(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.length();
+ END;
+
+ --nestingLevel;
+ return Value::fromObject(context->engine->newArrayObject(context, 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->identifier(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(ctx);
+ 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;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ Value v = o->getValueChecked(ctx, pd);
+ 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->array.length();
+ for (uint i = 0; i < len; ++i) {
+ bool exists;
+ Value v = a->__get__(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()
+{
+ 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(ExecutionContext *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(ExecutionContext *ctx)
+{
+ Stringify stringify(ctx);
+
+ Object *o = ctx->argument(1).asObject();
+ if (o) {
+ stringify.replacerFunction = o->asFunctionObject();
+ if (o->isArrayObject()) {
+ for (uint i = 0; i < o->array.length(); ++i) {
+ Value v = o->__get__(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(ctx)), ' ');
+ } 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/v4/qv4jsonobject.h b/src/v4/qv4jsonobject.h
new file mode 100644
index 0000000000..7c1df7c40b
--- /dev/null
+++ b/src/v4/qv4jsonobject.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct JsonObject : Object {
+ JsonObject(ExecutionContext *context);
+
+ static Value method_parse(ExecutionContext *ctx);
+ static Value method_stringify(ExecutionContext *ctx);
+
+};
+
+}
+}
+
+#endif
+
diff --git a/src/v4/qv4managed.cpp b/src/v4/qv4managed.cpp
new file mode 100644
index 0000000000..8156878508
--- /dev/null
+++ b/src/v4/qv4managed.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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;
+
+Managed::~Managed()
+{
+ nextFree = 0;
+ inUse = 0;
+}
+
+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->~Managed();
+}
+
+
+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 (static_cast<const ErrorObject *>(this)->errorType) {
+ 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);
+}
diff --git a/src/v4/qv4managed.h b/src/v4/qv4managed.h
new file mode 100644
index 0000000000..dc4d1f2243
--- /dev/null
+++ b/src/v4/qv4managed.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** 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 <wtf/Platform.h>
+#include "qv4global.h"
+
+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 Q_V4_EXPORT Managed
+{
+private:
+ void *operator new(size_t);
+ Managed(const Managed &other);
+ void operator = (const Managed &other);
+
+protected:
+ Managed()
+ : markBit(0)
+ , inUse(1)
+ , extensible(1)
+ , isNonStrictArgumentsObject(0)
+ , isBuiltinFunction(0)
+ , needsActivation(0)
+ , usesArgumentsObject(0)
+ , strictMode(0)
+ , type(Type_Invalid)
+ , unused(0)
+ , stringHash(0)
+ {}
+ virtual ~Managed();
+
+public:
+ void *operator new(size_t size, MemoryManager *mm);
+ void operator delete(void *ptr);
+
+ inline void mark() {
+ if (markBit)
+ return;
+ markBit = 1;
+ if (type != Type_String)
+ markObjects();
+ }
+
+ 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
+ };
+
+ 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; }
+
+ bool isArrayObject() const { return type == Type_ArrayObject; }
+ bool isStringObject() const { return type == Type_StringObject; }
+
+ QString className() const;
+
+protected:
+ virtual void markObjects() {}
+
+ union {
+ Managed *nextFree;
+ struct {
+ quintptr markBit : 1;
+ quintptr inUse : 1;
+ quintptr extensible : 1; // used by Object
+ quintptr isNonStrictArgumentsObject : 1;
+ quintptr isBuiltinFunction : 1; // used by FunctionObject
+ quintptr needsActivation : 1; // used by FunctionObject
+ quintptr usesArgumentsObject : 1; // used by FunctionObject
+ quintptr strictMode : 1; // used by FunctionObject
+ quintptr type : 8;
+ quintptr unused : 16;
+ mutable quintptr stringHash : 32;
+ };
+ };
+
+private:
+ friend class MemoryManager;
+ friend struct ExecutionContext;
+};
+
+}
+}
+
+#endif
diff --git a/src/v4/qv4mathobject.cpp b/src/v4/qv4mathobject.cpp
new file mode 100644
index 0000000000..e40faba861
--- /dev/null
+++ b/src/v4/qv4mathobject.cpp
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** 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 <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)
+{
+ type = Type_MathObject;
+
+ 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(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v == 0) // 0 | -0
+ return Value::fromDouble(0);
+
+ return Value::fromDouble(v < 0 ? -v : v);
+}
+
+Value MathObject::method_acos(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v > 1)
+ return Value::fromDouble(qSNaN());
+
+ return Value::fromDouble(::acos(v));
+}
+
+Value MathObject::method_asin(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v > 1)
+ return Value::fromDouble(qSNaN());
+ else
+ return Value::fromDouble(::asin(v));
+}
+
+Value MathObject::method_atan(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v == 0.0)
+ return Value::fromDouble(v);
+ else
+ return Value::fromDouble(::atan(v));
+}
+
+Value MathObject::method_atan2(ExecutionContext *ctx)
+{
+ double v1 = ctx->argument(0).toNumber(ctx);
+ double v2 = ctx->argument(1).toNumber(ctx);
+ 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(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v < 0.0 && v > -1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(::ceil(v));
+}
+
+Value MathObject::method_cos(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ return Value::fromDouble(::cos(v));
+}
+
+Value MathObject::method_exp(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ 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(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ return Value::fromDouble(::floor(v));
+}
+
+Value MathObject::method_log(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v < 0)
+ return Value::fromDouble(qSNaN());
+ else
+ return Value::fromDouble(::log(v));
+}
+
+Value MathObject::method_max(ExecutionContext *ctx)
+{
+ double mx = -qInf();
+ for (unsigned i = 0; i < ctx->argumentCount; ++i) {
+ double x = ctx->argument(i).toNumber(ctx);
+ if (x > mx || std::isnan(x))
+ mx = x;
+ }
+ return Value::fromDouble(mx);
+}
+
+Value MathObject::method_min(ExecutionContext *ctx)
+{
+ double mx = qInf();
+ for (unsigned i = 0; i < ctx->argumentCount; ++i) {
+ double x = ctx->argument(i).toNumber(ctx);
+ if ((x == 0 && mx == x && copySign(1.0, x) == -1.0)
+ || (x < mx) || std::isnan(x)) {
+ mx = x;
+ }
+ }
+ return Value::fromDouble(mx);
+}
+
+Value MathObject::method_pow(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ double x = argc > 0 ? argv[0].toNumber(parentCtx) : qSNaN();
+ double y = argc > 1 ? argv[1].toNumber(parentCtx) : qSNaN();
+
+ if (std::isnan(y))
+ return Value::fromDouble(qSNaN());
+
+ if (y == 0) {
+ return Value::fromDouble(1);
+ } else if (((x == 1) || (x == -1)) && std::isinf(y)) {
+ return Value::fromDouble(qSNaN());
+ } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) {
+ return Value::fromDouble(qInf());
+ } else if ((x == 0) && copySign(1.0, x) == -1.0) {
+ if (y < 0) {
+ if (::fmod(-y, 2.0) == 1.0)
+ return Value::fromDouble(-qInf());
+ else
+ return Value::fromDouble(qInf());
+ } else if (y > 0) {
+ if (::fmod(y, 2.0) == 1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(0);
+ }
+ }
+
+#ifdef Q_OS_AIX
+ else if (qIsInf(x) && copySign(1.0, x) == -1.0) {
+ if (y > 0) {
+ if (::fmod(y, 2.0) == 1.0)
+ return Value::fromDouble(-qInf());
+ else
+ return Value::fromDouble(qInf());
+ } else if (y < 0) {
+ if (::fmod(-y, 2.0) == 1.0)
+ return Value::fromDouble(copySign(0, -1.0));
+ else
+ return Value::fromDouble(0);
+ }
+ }
+#endif
+ else {
+ return Value::fromDouble(::pow(x, y));
+ }
+ // ###
+ return Value::fromDouble(qSNaN());
+}
+
+Value MathObject::method_random(ExecutionContext */*ctx*/)
+{
+ return Value::fromDouble(qrand() / (double) RAND_MAX);
+}
+
+Value MathObject::method_round(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ v = copySign(::floor(v + 0.5), v);
+ return Value::fromDouble(v);
+}
+
+Value MathObject::method_sin(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ return Value::fromDouble(::sin(v));
+}
+
+Value MathObject::method_sqrt(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ return Value::fromDouble(::sqrt(v));
+}
+
+Value MathObject::method_tan(ExecutionContext *ctx)
+{
+ double v = ctx->argument(0).toNumber(ctx);
+ if (v == 0.0)
+ return Value::fromDouble(v);
+ else
+ return Value::fromDouble(::tan(v));
+}
+
diff --git a/src/v4/qv4mathobject.h b/src/v4/qv4mathobject.h
new file mode 100644
index 0000000000..c8428d2942
--- /dev/null
+++ b/src/v4/qv4mathobject.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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"
+
+namespace QQmlJS {
+
+namespace VM {
+
+struct MathObject: Object
+{
+ MathObject(ExecutionContext *ctx);
+ virtual QString className() { return QStringLiteral("Math"); }
+
+ static Value method_abs(ExecutionContext *ctx);
+ static Value method_acos(ExecutionContext *ctx);
+ static Value method_asin(ExecutionContext *ctx);
+ static Value method_atan(ExecutionContext *ctx);
+ static Value method_atan2(ExecutionContext *ctx);
+ static Value method_ceil(ExecutionContext *ctx);
+ static Value method_cos(ExecutionContext *ctx);
+ static Value method_exp(ExecutionContext *ctx);
+ static Value method_floor(ExecutionContext *ctx);
+ static Value method_log(ExecutionContext *ctx);
+ static Value method_max(ExecutionContext *ctx);
+ static Value method_min(ExecutionContext *ctx);
+ static Value method_pow(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_random(ExecutionContext *ctx);
+ static Value method_round(ExecutionContext *ctx);
+ static Value method_sin(ExecutionContext *ctx);
+ static Value method_sqrt(ExecutionContext *ctx);
+ static Value method_tan(ExecutionContext *ctx);
+};
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/v4/qv4mm.cpp b/src/v4/qv4mm.cpp
new file mode 100644
index 0000000000..c77ec1eaba
--- /dev/null
+++ b/src/v4/qv4mm.cpp
@@ -0,0 +1,396 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#include "qmljs_engine.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 <alloca.h>
+
+using namespace QQmlJS::VM;
+using namespace WTF;
+
+static const std::size_t CHUNK_SIZE = 65536;
+
+struct MemoryManager::Data
+{
+ bool enableGC;
+ bool gcBlocked;
+ bool scribble;
+ bool aggressiveGC;
+ ExecutionEngine *engine;
+
+ enum { MaxItemSize = 256 };
+ Managed *smallItems[MaxItemSize/16];
+ uint nChunks[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)
+ {
+ memset(smallItems, 0, sizeof(smallItems));
+ memset(nChunks, 0, sizeof(nChunks));
+ scribble = qgetenv("MM_NO_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();
+ }
+};
+
+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))
+{
+ setEnableGC(true);
+}
+
+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;
+
+ // 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->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 = std::max(size, CHUNK_SIZE*(1 << shift));
+ allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize);
+ Data::Chunk allocation;
+ allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages);
+ allocation.chunkSize = size;
+ m_d->heapChunks.append(allocation);
+ qSort(m_d->heapChunks);
+ char *chunk = (char *)allocation.memory.base();
+ char *end = chunk + allocation.memory.size() - size;
+ memset(chunk, 0, allocation.memory.size());
+ Managed **last = &m_d->smallItems[pos];
+ while (chunk <= end) {
+ Managed *o = reinterpret_cast<Managed *>(chunk);
+ o->inUse = 0;
+ o->markBit = 0;
+ *last = o;
+ last = &o->nextFree;
+ chunk += size;
+ }
+ *last = 0;
+ m = m_d->smallItems[pos];
+ }
+
+ found:
+ m_d->smallItems[pos] = m->nextFree;
+ return m;
+}
+
+void MemoryManager::scribble(Managed *obj, int c, int size) const
+{
+ if (m_d->scribble)
+ ::memset((void *)(obj + 1), c, size - sizeof(Managed));
+}
+
+void MemoryManager::mark()
+{
+ m_d->engine->markObjects();
+
+ for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent)
+ ctxt->mark();
+
+ for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it)
+ it.key()->mark();
+
+ collectFromStack();
+
+ return;
+}
+
+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);
+
+ 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];
+
+ for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; ) {
+ 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);
+
+ chunk = chunk + size;
+ if (m->inUse) {
+ if (m->markBit) {
+ m->markBit = 0;
+ } else {
+// qDebug() << "-- collecting it." << m << *f << &m->nextFree;
+ m->~Managed();
+
+ m->nextFree = *f;
+ f = &m->nextFree;
+ //scribble(m, 0x99, size);
+ ++freedCount;
+ }
+ }
+ }
+
+ 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;
+}
+
+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
+{
+ if (!m_d->heapChunks.count())
+ return;
+
+ quintptr valueOnStack = 0;
+
+#if USE(PTHREADS)
+#if OS(DARWIN)
+ void* stackTop = 0;
+ stackTop = pthread_get_stackaddr_np(pthread_self());
+ quintptr *top = static_cast<quintptr *>(stackTop);
+#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);
+
+ quintptr *top = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr);
+#endif
+#endif
+// qDebug() << "stack:" << hex << stackTop << stackSize << (stackTop + stackSize);
+
+ quintptr *current = (&valueOnStack) + 1;
+// qDebug() << "collectFromStack" << top << current << &valueOnStack;
+
+ 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());
+ heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) + it->memory.size();
+ }
+
+ for (; current < top; ++current) {
+ char* genericPtr =
+#if CPU(X86_64)
+ 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.
+ if (index & 1) {
+ int size = m_d->heapChunks.at(index >> 1).chunkSize;
+ Managed *m = reinterpret_cast<Managed *>(genericPtr);
+// qDebug() << " inside" << size << m;
+
+ if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1]) % size)
+ // wrongly aligned value, skip it
+ continue;
+
+ if (!m->inUse)
+ // Skip pointers to already freed objects, they are bogus as well
+ continue;
+
+ m->mark();
+// qDebug() << " marking";
+ }
+ }
+}
diff --git a/src/v4/qv4mm.h b/src/v4/qv4mm.h
new file mode 100644
index 0000000000..6f747f53d2
--- /dev/null
+++ b/src/v4/qv4mm.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance 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.
+**
+****************************************************************************/
+
+#ifndef QV4GC_H
+#define QV4GC_H
+
+#include "qv4global.h"
+#include "qv4object.h"
+
+#include <QScopedPointer>
+
+#define DETAILED_MM_STATS
+
+namespace QQmlJS {
+namespace VM {
+
+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;
+ }
+
+ 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);
+
+ void scribble(Managed *obj, int c, int size) const;
+
+ 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;
+};
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QV4GC_H
diff --git a/src/v4/qv4numberobject.cpp b/src/v4/qv4numberobject.cpp
new file mode 100644
index 0000000000..08711a8187
--- /dev/null
+++ b/src/v4/qv4numberobject.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** 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;
+
+
+NumberCtor::NumberCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value NumberCtor::construct(ExecutionContext *ctx)
+{
+ double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 0;
+ return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d)));
+}
+
+Value NumberCtor::call(ExecutionContext *ctx)
+{
+ double d = ctx->argumentCount ? ctx->argument(0).toNumber(ctx) : 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(ExecutionContext *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(ctx);
+ if (radix < 2 || radix > 36) {
+ ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix")
+ .arg(radix));
+ return Value::undefinedValue();
+ }
+
+ if (std::isnan(num)) {
+ return Value::fromString(ctx, QStringLiteral("NaN"));
+ } else if (qIsInf(num)) {
+ return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity"));
+ }
+
+ if (radix != 10) {
+ QString str;
+ bool negative = false;
+ if (num < 0) {
+ negative = true;
+ num = -num;
+ }
+ double frac = num - ::floor(num);
+ num = Value::toInteger(num);
+ do {
+ char c = (char)::fmod(num, radix);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ str.prepend(QLatin1Char(c));
+ num = ::floor(num / radix);
+ } while (num != 0);
+ if (frac != 0) {
+ str.append(QLatin1Char('.'));
+ do {
+ frac = frac * radix;
+ char c = (char)::floor(frac);
+ c = (c < 10) ? (c + '0') : (c - 10 + 'a');
+ str.append(QLatin1Char(c));
+ frac = frac - ::floor(frac);
+ } while (frac != 0);
+ }
+ if (negative)
+ str.prepend(QLatin1Char('-'));
+ return Value::fromString(ctx, str);
+ }
+ }
+
+ String *str = Value::fromDouble(num).toString(ctx);
+ return Value::fromString(str);
+}
+
+Value NumberPrototype::method_toLocaleString(ExecutionContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ String *str = thisObject->value.toString(ctx);
+ return Value::fromString(str);
+}
+
+Value NumberPrototype::method_valueOf(ExecutionContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ return thisObject->value;
+}
+
+Value NumberPrototype::method_toFixed(ExecutionContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger(ctx);
+
+ if (std::isnan(fdigits))
+ fdigits = 0;
+
+ if (fdigits < 0 || fdigits > 20)
+ ctx->throwRangeError(ctx->thisObject);
+
+ double v = thisObject->value.asDouble();
+ QString str;
+ if (std::isnan(v))
+ str = QString::fromLatin1("NaN");
+ else if (qIsInf(v))
+ str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity");
+ else
+ str = QString::number(v, 'f', int (fdigits));
+ return Value::fromString(ctx, str);
+}
+
+Value NumberPrototype::method_toExponential(ExecutionContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger(ctx);
+
+ QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits));
+ return Value::fromString(ctx, z);
+}
+
+Value NumberPrototype::method_toPrecision(ExecutionContext *ctx)
+{
+ NumberObject *thisObject = ctx->thisObject.asNumberObject();
+ if (!thisObject)
+ ctx->throwTypeError();
+
+ double fdigits = 0;
+
+ if (ctx->argumentCount > 0)
+ fdigits = ctx->argument(0).toInteger(ctx);
+
+ return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits)));
+}
diff --git a/src/v4/qv4numberobject.h b/src/v4/qv4numberobject.h
new file mode 100644
index 0000000000..85f9e1ff2b
--- /dev/null
+++ b/src/v4/qv4numberobject.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct NumberCtor: FunctionObject
+{
+ NumberCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct NumberPrototype: NumberObject
+{
+ NumberPrototype(): NumberObject(Value::fromDouble(0)) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_toLocaleString(ExecutionContext *ctx);
+ static Value method_valueOf(ExecutionContext *ctx);
+ static Value method_toFixed(ExecutionContext *ctx);
+ static Value method_toExponential(ExecutionContext *ctx);
+ static Value method_toPrecision(ExecutionContext *ctx);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp
new file mode 100644
index 0000000000..dadf8479c2
--- /dev/null
+++ b/src/v4/qv4object.cpp
@@ -0,0 +1,674 @@
+/****************************************************************************
+**
+** 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 "qv4ir_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 <qv4ir_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 <alloca.h>
+
+using namespace QQmlJS::VM;
+
+
+//
+// Object
+//
+Object::~Object()
+{
+}
+
+void Object::__put__(ExecutionContext *ctx, const QString &name, const Value &value)
+{
+ __put__(ctx, ctx->engine->identifier(name), value);
+}
+
+Value Object::getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const
+{
+ if (p->isData())
+ return p->value;
+ if (!p->get)
+ return Value::undefinedValue();
+
+ return p->get->call(ctx, Value::fromObject(const_cast<Object *>(this)), 0, 0);
+}
+
+Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const
+{
+ if (!p || p->type == PropertyDescriptor::Generic)
+ return Value::undefinedValue();
+ return getValue(ctx, p);
+}
+
+Value Object::getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const
+{
+ *exists = p && p->type != PropertyDescriptor::Generic;
+ if (!*exists)
+ return Value::undefinedValue();
+ return getValue(ctx, p);
+}
+
+void Object::inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx)
+{
+ bool hasProperty = false;
+ Value v = __get__(ctx, name, &hasProperty);
+ Value result = op(v, rhs, ctx);
+ __put__(ctx, name, result);
+}
+
+void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx)
+{
+ uint idx = index.asArrayIndex();
+ if (idx < UINT_MAX) {
+ bool hasProperty = false;
+ Value v = __get__(ctx, idx, &hasProperty);
+ v = op(v, rhs, ctx);
+ __put__(ctx, idx, v);
+ return;
+ }
+ String *name = index.toString(ctx);
+ assert(name);
+ inplaceBinOp(rhs, name, op, ctx);
+}
+
+void Object::defineDefaultProperty(String *name, Value value)
+{
+ if (!members)
+ members.reset(new PropertyTable());
+ PropertyDescriptor *pd = members->insert(name);
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Disabled;
+ pd->configurable = PropertyDescriptor::Enabled;
+ pd->value = value;
+}
+
+void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value)
+{
+ defineDefaultProperty(context->engine->identifier(name), value);
+}
+
+void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *), int argumentCount)
+{
+ Q_UNUSED(argumentCount);
+ String *s = context->engine->identifier(name);
+ FunctionObject* function = context->engine->newBuiltinFunction(context, s, code);
+ function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount));
+ defineDefaultProperty(s, Value::fromObject(function));
+}
+
+void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount)
+{
+ Q_UNUSED(argumentCount);
+ String *s = context->engine->identifier(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->identifier(name), value);
+}
+
+void Object::defineReadonlyProperty(String *name, Value value)
+{
+ if (!members)
+ members.reset(new PropertyTable());
+ PropertyDescriptor *pd = members->insert(name);
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Disabled;
+ pd->enumberable = PropertyDescriptor::Disabled;
+ pd->configurable = PropertyDescriptor::Disabled;
+ pd->value = value;
+}
+
+void Object::markObjects()
+{
+ if (prototype)
+ prototype->mark();
+
+ if (members) {
+ for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) {
+ if (!(*it))
+ continue;
+ (*it)->name->mark();
+ PropertyDescriptor &pd = (*it)->descriptor;
+ if (pd.isData()) {
+ if (Managed *m = pd.value.asManaged())
+ m->mark();
+ } else if (pd.isAccessor()) {
+ if (pd.get)
+ pd.get->mark();
+ if (pd.set)
+ pd.set->mark();
+ }
+ }
+ }
+ array.markObjects();
+}
+
+// Section 8.12.1
+PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __getOwnProperty__(ctx, idx);
+
+ if (members)
+ return members->find(name);
+ return 0;
+}
+
+PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index)
+{
+ PropertyDescriptor *p = array.at(index);
+ if(p && p->type != PropertyDescriptor::Generic)
+ return p;
+ if (isStringObject())
+ return static_cast<StringObject *>(this)->getIndex(ctx, index);
+
+ return 0;
+}
+
+// Section 8.12.2
+PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __getPropertyDescriptor__(ctx, idx);
+
+
+ Object *o = this;
+ while (o) {
+ if (o->members) {
+ if (PropertyDescriptor *p = o->members->find(name))
+ return p;
+ }
+ o = o->prototype;
+ }
+ return 0;
+}
+
+PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, uint index)
+{
+ Object *o = this;
+ while (o) {
+ PropertyDescriptor *p = o->array.at(index);
+ if(p && p->type != PropertyDescriptor::Generic)
+ return p;
+ if (o->isStringObject()) {
+ p = static_cast<StringObject *>(o)->getIndex(ctx, index);
+ if (p)
+ return p;
+ }
+ o = o->prototype;
+ }
+ return 0;
+}
+
+// Section 8.12.3
+Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __get__(ctx, idx, hasProperty);
+
+ if (name->isEqualTo(ctx->engine->id___proto__)) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Value::fromObject(prototype);
+ }
+
+ if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name)) {
+ if (hasProperty)
+ *hasProperty = true;
+ return getValue(ctx, p);
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty)
+{
+ const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index);
+ if (p && p->type != PropertyDescriptor::Generic) {
+ if (hasProperty)
+ *hasProperty = true;
+ return getValue(ctx, p);
+ }
+
+ if (hasProperty)
+ *hasProperty = false;
+ return Value::undefinedValue();
+}
+
+
+// Section 8.12.5
+void Object::__put__(ExecutionContext *ctx, String *name, Value value)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __put__(ctx, idx, value);
+
+ PropertyDescriptor *pd = __getOwnProperty__(ctx, name);
+ // clause 1
+ if (pd) {
+ if (pd->isAccessor()) {
+ if (pd->set)
+ goto cont;
+ goto reject;
+ } else if (!pd->isWritable())
+ goto reject;
+ else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) {
+ bool ok;
+ uint l = value.asArrayLength(ctx, &ok);
+ if (!ok)
+ ctx->throwRangeError(value);
+ ok = array.setLength(l);
+ if (!ok)
+ goto reject;
+ } else {
+ pd->value = value;
+ }
+ return;
+ } else if (!prototype) {
+ if (!extensible)
+ goto reject;
+ } else {
+ if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) {
+ if (p->isAccessor()) {
+ if (p->set)
+ goto cont;
+ goto reject;
+ }
+ if (!extensible)
+ goto reject;
+ if (!p->isWritable())
+ goto reject;
+ } else {
+ if (!extensible)
+ goto reject;
+ }
+ }
+
+ cont:
+
+ if (!members)
+ members.reset(new PropertyTable());
+
+
+ // clause 4
+ if (!pd && prototype)
+ pd = prototype->__getPropertyDescriptor__(ctx, name);
+
+ // Clause 5
+ if (pd && pd->isAccessor()) {
+ assert(pd->set != 0);
+
+ Value args[1];
+ args[0] = value;
+ pd->set->call(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+
+ {
+ PropertyDescriptor *p = members->insert(name);
+ p->type = PropertyDescriptor::Data;
+ p->value = value;
+ p->configurable = PropertyDescriptor::Enabled;
+ p->enumberable = PropertyDescriptor::Enabled;
+ p->writable = PropertyDescriptor::Enabled;
+ return;
+ }
+
+ reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+}
+
+void Object::__put__(ExecutionContext *ctx, uint index, Value value)
+{
+ PropertyDescriptor *pd = __getOwnProperty__(ctx, index);
+ // clause 1
+ if (pd) {
+ if (pd->isAccessor()) {
+ if (pd->set)
+ goto cont;
+ goto reject;
+ } else if (!pd->isWritable())
+ goto reject;
+ else
+ pd->value = value;
+ return;
+ } else if (!prototype) {
+ if (!extensible)
+ goto reject;
+ } else {
+ if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) {
+ if (p->isAccessor()) {
+ if (p->set)
+ goto cont;
+ goto reject;
+ }
+ if (!extensible)
+ goto reject;
+ if (!p->isWritable())
+ goto reject;
+ } else {
+ if (!extensible)
+ goto reject;
+ }
+ }
+
+ cont:
+
+ // clause 4
+ if (!pd && prototype)
+ pd = prototype->__getPropertyDescriptor__(ctx, index);
+
+ // Clause 5
+ if (pd && pd->isAccessor()) {
+ assert(pd->set != 0);
+
+ Value args[1];
+ args[0] = value;
+ pd->set->call(ctx, Value::fromObject(this), args, 1);
+ return;
+ }
+
+ array.set(index, value);
+ return;
+
+ reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+}
+
+// Section 8.12.6
+bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __hasProperty__(ctx, idx);
+
+ if (members && members->find(name) != 0)
+ return true;
+
+ return prototype ? prototype->__hasProperty__(ctx, name) : false;
+}
+
+bool Object::__hasProperty__(const ExecutionContext *ctx, uint index) const
+{
+ const PropertyDescriptor *p = array.at(index);
+ if (p && p->type != PropertyDescriptor::Generic)
+ return true;
+
+ return prototype ? prototype->__hasProperty__(ctx, index) : false;
+}
+
+// Section 8.12.7
+bool Object::__delete__(ExecutionContext *ctx, String *name)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __delete__(ctx, idx);
+
+ if (members) {
+ if (PropertyTableEntry *entry = members->findEntry(name)) {
+ if (entry->descriptor.isConfigurable()) {
+ members->remove(entry);
+ return true;
+ }
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Object::__delete__(ExecutionContext *ctx, uint index)
+{
+ if (array.deleteIndex(index))
+ return true;
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+ return false;
+}
+
+// Section 8.12.9
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc)
+{
+ uint idx = name->asArrayIndex();
+ if (idx != String::InvalidArrayIndex)
+ return __defineOwnProperty__(ctx, idx, desc);
+
+ PropertyDescriptor *current;
+
+ if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) {
+ PropertyDescriptor *lp = array.getLengthProperty();
+ if (desc->isEmpty() || desc->isSubset(lp))
+ return true;
+ if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable())
+ goto reject;
+ bool succeeded = true;
+ if (desc->type == PropertyDescriptor::Data) {
+ bool ok;
+ uint l = desc->value.asArrayLength(ctx, &ok);
+ if (!ok)
+ ctx->throwRangeError(desc->value);
+ succeeded = array.setLength(l);
+ }
+ if (desc->writable == PropertyDescriptor::Disabled)
+ lp->writable = PropertyDescriptor::Disabled;
+ if (!succeeded)
+ goto reject;
+ return true;
+ }
+
+ if (!members)
+ members.reset(new PropertyTable());
+
+ // Clause 1
+ current = __getOwnProperty__(ctx, name);
+ if (!current) {
+ // clause 3
+ if (!extensible)
+ goto reject;
+ // clause 4
+ PropertyDescriptor *pd = members->insert(name);
+ *pd = *desc;
+ pd->fullyPopulated();
+ return true;
+ }
+
+ return __defineOwnProperty__(ctx, current, desc);
+reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+ return false;
+}
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc)
+{
+ PropertyDescriptor *current;
+
+ // 15.4.5.1, 4b
+ if (isArrayObject() && index >= array.length() && !array.getLengthProperty()->isWritable())
+ goto reject;
+
+ if (isNonStrictArgumentsObject)
+ return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, desc);
+
+ // Clause 1
+ current = __getOwnProperty__(ctx, index);
+ if (!current) {
+ // clause 3
+ if (!extensible)
+ goto reject;
+ // clause 4
+ PropertyDescriptor *pd = array.insert(index);
+ *pd = *desc;
+ pd->fullyPopulated();
+ return true;
+ }
+
+ return __defineOwnProperty__(ctx, current, desc);
+reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+ return false;
+}
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc)
+{
+ // clause 5
+ if (desc->isEmpty())
+ return true;
+
+ // clause 6
+ if (desc->isSubset(current))
+ return true;
+
+ // clause 7
+ if (!current->isConfigurable()) {
+ if (desc->isConfigurable())
+ goto reject;
+ if (desc->enumberable != PropertyDescriptor::Undefined && desc->enumberable != current->enumberable)
+ goto reject;
+ }
+
+ // clause 8
+ if (desc->isGeneric())
+ goto accept;
+
+ // clause 9
+ if (current->isData() != desc->isData()) {
+ // 9a
+ if (!current->isConfigurable())
+ goto reject;
+ if (current->isData()) {
+ // 9b
+ current->type = PropertyDescriptor::Accessor;
+ current->writable = PropertyDescriptor::Undefined;
+ current->get = 0;
+ current->set = 0;
+ } else {
+ // 9c
+ current->type = PropertyDescriptor::Data;
+ current->writable = PropertyDescriptor::Disabled;
+ current->value = Value::undefinedValue();
+ }
+ } else if (current->isData() && desc->isData()) { // clause 10
+ if (!current->isConfigurable() && !current->isWritable()) {
+ if (desc->isWritable() || !current->value.sameValue(desc->value))
+ goto reject;
+ }
+ } else { // clause 10
+ assert(current->isAccessor() && desc->isAccessor());
+ if (!current->isConfigurable()) {
+ if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1)))
+ goto reject;
+ if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1)))
+ goto reject;
+ }
+ }
+
+ accept:
+
+ *current += *desc;
+ return true;
+ reject:
+ if (ctx->strictMode)
+ __qmljs_throw_type_error(ctx);
+ return false;
+}
+
+
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc)
+{
+ return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc);
+}
+
+
+void ArrayObject::init(ExecutionContext *context)
+{
+ type = Type_ArrayObject;
+
+ if (!members)
+ members.reset(new PropertyTable());
+ PropertyDescriptor *pd = members->insert(context->engine->id_length);
+ pd->type = PropertyDescriptor::Data;
+ pd->writable = PropertyDescriptor::Enabled;
+ pd->enumberable = PropertyDescriptor::Disabled;
+ pd->configurable = PropertyDescriptor::Disabled;
+ pd->value = Value::fromInt32(0);
+ array.setLengthProperty(pd);
+}
+
+
+
+void ForEachIteratorObject::markObjects()
+{
+ Object::markObjects();
+ if (it.object)
+ it.object->mark();
+}
+
diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h
new file mode 100644
index 0000000000..73d62bf209
--- /dev/null
+++ b/src/v4/qv4object.h
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qmljs_engine.h"
+#include "qmljs_environment.h"
+#include "qv4array.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4propertydescriptor.h"
+#include "qv4propertytable.h"
+#include "qv4objectiterator.h"
+#include "qv4regexp.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+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 Q_V4_EXPORT Object: Managed {
+ Object *prototype;
+ QScopedPointer<PropertyTable> members;
+ Array array;
+
+ Object()
+ : prototype(0) { type = Type_Object; }
+ Object(const Array &a)
+ : prototype(0), array(a) {}
+
+ virtual ~Object();
+
+ PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name);
+ PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index);
+ PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name);
+ PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index);
+
+ Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0);
+ Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0);
+
+ void __put__(ExecutionContext *ctx, String *name, Value value);
+ void __put__(ExecutionContext *ctx, uint index, Value value);
+
+ bool __hasProperty__(const ExecutionContext *ctx, String *name) const;
+ bool __hasProperty__(const ExecutionContext *ctx, uint index) const;
+ bool __delete__(ExecutionContext *ctx, String *name);
+ bool __delete__(ExecutionContext *ctx, uint index);
+ bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc);
+ bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc);
+ bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc);
+ bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc);
+
+ //
+ // helpers
+ //
+ void __put__(ExecutionContext *ctx, const QString &name, const Value &value);
+
+ Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const;
+ Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const;
+ Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const;
+
+ void inplaceBinOp(Value rhs, String *name, BinOp op, ExecutionContext *ctx);
+ void inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx);
+
+ /* 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)(ExecutionContext *), int count = 0);
+ void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(ExecutionContext *, Value, Value *, int), int argumentCount = 0);
+ /* Fixed: Writable: false, Enumerable: false, Configurable: false */
+ void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value);
+ void defineReadonlyProperty(String *name, Value value);
+
+protected:
+ virtual void markObjects();
+
+ friend struct ObjectIterator;
+};
+
+struct ForEachIteratorObject: Object {
+ ObjectIterator it;
+ ForEachIteratorObject(ExecutionContext *ctx, Object *o)
+ : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; }
+
+ Value nextPropertyName() { return it.nextPropertyNameAsString(); }
+
+protected:
+ virtual void markObjects();
+};
+
+struct BooleanObject: Object {
+ Value value;
+ BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; }
+};
+
+struct NumberObject: Object {
+ Value value;
+ NumberObject(const Value &value): value(value) { type = Type_NumberObject; }
+};
+
+struct ArrayObject: Object {
+ ArrayObject(ExecutionContext *ctx) { init(ctx); }
+ ArrayObject(ExecutionContext *ctx, const Array &value): Object(value) { init(ctx); array.setLengthUnchecked(array.length()); }
+ void init(ExecutionContext *context);
+};
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp
new file mode 100644
index 0000000000..8da2e7bdba
--- /dev/null
+++ b/src/v4/qv4objectiterator.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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"
+
+namespace QQmlJS {
+namespace VM {
+
+ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags)
+ : context(context)
+ , object(o)
+ , current(o)
+ , arrayNode(0)
+ , arrayIndex(0)
+ , tableIndex(0)
+ , flags(flags)
+{
+ if (current && current->asStringObject())
+ this->flags |= CurrentIsString;
+}
+
+PropertyDescriptor *ObjectIterator::next(String **name, uint *index)
+{
+ PropertyDescriptor *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;
+ return s->__getOwnProperty__(context, *index);
+ }
+ flags &= ~CurrentIsString;
+ arrayNode = current->array.sparseBegin();
+ // iterate until we're past the end of the string
+ while (arrayNode && arrayNode->key() < slen)
+ arrayNode = arrayNode->nextNode();
+ }
+
+ if (!arrayIndex)
+ arrayNode = current->array.sparseBegin();
+
+ // sparse arrays
+ if (arrayNode) {
+ while (arrayNode != current->array.sparseEnd()) {
+ int k = arrayNode->key();
+ p = current->array.at(k);
+ arrayNode = arrayNode->nextNode();
+ if (p && (!(flags & EnumberableOnly) || p->isEnumerable())) {
+ arrayIndex = k + 1;
+ *index = k;
+ return p;
+ }
+ }
+ arrayNode = 0;
+ arrayIndex = UINT_MAX;
+ }
+ // dense arrays
+ while (arrayIndex < current->array.length()) {
+ p = current->array.at(arrayIndex);
+ ++arrayIndex;
+ if (p && p->type != PropertyDescriptor::Generic && (!(flags & EnumberableOnly) || p->isEnumerable())) {
+ *index = arrayIndex - 1;
+ return p;
+ }
+ }
+
+ if (!current->members || tableIndex >= (uint)current->members->_propertyCount) {
+ if (flags & WithProtoChain)
+ current = current->prototype;
+ else
+ current = 0;
+ if (current && current->asStringObject())
+ flags |= CurrentIsString;
+ else
+ flags &= ~CurrentIsString;
+
+
+ arrayIndex = 0;
+ tableIndex = 0;
+ continue;
+ }
+ PropertyTableEntry *pt = current->members->_properties[tableIndex];
+ ++tableIndex;
+ // ### check that it's not a repeated attribute
+ if (pt && (!(flags & EnumberableOnly) || pt->descriptor.isEnumerable())) {
+ *name = pt->name;
+ p = &pt->descriptor;
+ 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/v4/qv4objectiterator.h b/src/v4/qv4objectiterator.h
new file mode 100644
index 0000000000..baecc25424
--- /dev/null
+++ b/src/v4/qv4objectiterator.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 QV4OBJECTITERATOR_H
+#define QV4OBJECTITERATOR_H
+
+#include "qmljs_value.h"
+
+namespace QQmlJS {
+namespace VM {
+
+struct SparseArrayNode;
+struct Object;
+struct PropertyDescriptor;
+
+struct ObjectIterator
+{
+ enum Flags {
+ NoFlags = 0,
+ EnumberableOnly = 0x1,
+ WithProtoChain = 0x2,
+ CurrentIsString = 0x4
+ };
+
+ ExecutionContext *context;
+ Object *object;
+ Object *current;
+ SparseArrayNode *arrayNode;
+ uint arrayIndex;
+ uint tableIndex;
+ uint flags;
+
+ ObjectIterator(ExecutionContext *context, Object *o, uint flags);
+ PropertyDescriptor *next(String **name, uint *index);
+ Value nextPropertyName();
+ Value nextPropertyNameAsString();
+};
+
+}
+}
+
+#endif
diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp
new file mode 100644
index 0000000000..98f205d1d8
--- /dev/null
+++ b/src/v4/qv4objectproto.cpp
@@ -0,0 +1,558 @@
+/****************************************************************************
+**
+** 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 <qv4ir_p.h>
+#include <qv4codegen_p.h>
+#include <qv4isel_masm_p.h>
+
+#ifndef Q_WS_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;
+
+
+//
+// Object
+//
+ObjectCtor::ObjectCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value ObjectCtor::construct(ExecutionContext *ctx)
+{
+ if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull())
+ return ctx->thisObject;
+ return __qmljs_to_object(ctx->argument(0), ctx);
+}
+
+Value ObjectCtor::call(ExecutionContext *ctx)
+{
+ if (!ctx->argumentCount || ctx->argument(0).isUndefined() || ctx->argument(0).isNull())
+ return Value::fromObject(ctx->engine->newObject());
+ return __qmljs_to_object(ctx->argument(0), ctx);
+}
+
+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(ExecutionContext *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(ExecutionContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ String *name = ctx->argument(1).toString(ctx);
+ PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name);
+ return fromPropertyDescriptor(ctx, desc);
+}
+
+Value ObjectPrototype::method_getOwnPropertyNames(ExecutionContext *ctx)
+{
+ Object *O = ctx->argument(0).asObject();
+ if (!O)
+ ctx->throwTypeError();
+
+ ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject();
+ Array &a = array->array;
+ ObjectIterator it(ctx, O, ObjectIterator::NoFlags);
+ while (1) {
+ Value v = it.nextPropertyNameAsString();
+ if (v.isNull())
+ break;
+ a.push_back(v);
+ }
+ return Value::fromObject(array);
+}
+
+Value ObjectPrototype::method_create(ExecutionContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject() && !O.isNull())
+ ctx->throwTypeError();
+
+ Object *newObject = ctx->engine->newObject();
+ newObject->prototype = O.objectValue();
+
+ 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(ExecutionContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ String *name = ctx->argument(1).toString(ctx);
+
+ Value attributes = ctx->argument(2);
+ PropertyDescriptor pd;
+ toPropertyDescriptor(ctx, attributes, &pd);
+
+ if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd))
+ __qmljs_throw_type_error(ctx);
+
+ return O;
+}
+
+Value ObjectPrototype::method_defineProperties(ExecutionContext *ctx)
+{
+ Value O = ctx->argument(0);
+ if (!O.isObject())
+ ctx->throwTypeError();
+
+ Object *o = ctx->argument(1).toObject(ctx).objectValue();
+
+ ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ PropertyDescriptor n;
+ toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n);
+ bool ok;
+ if (name)
+ ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n);
+ else
+ ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n);
+ if (!ok)
+ __qmljs_throw_type_error(ctx);
+ }
+
+ return O;
+}
+
+Value ObjectPrototype::method_seal(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+
+ ObjectIterator it(ctx, o, ObjectIterator::NoFlags);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ pd->configurable = PropertyDescriptor::Disabled;
+ }
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_freeze(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+
+ ObjectIterator it(ctx, o, ObjectIterator::NoFlags);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ if (pd->type == PropertyDescriptor::Data)
+ pd->writable = PropertyDescriptor::Disabled;
+ pd->configurable = PropertyDescriptor::Disabled;
+ }
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_preventExtensions(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ o->extensible = false;
+ return ctx->argument(0);
+}
+
+Value ObjectPrototype::method_isSealed(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ if (o->extensible)
+ return Value::fromBoolean(false);
+
+ ObjectIterator it(ctx, o, ObjectIterator::NoFlags);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ if (pd->configurable != PropertyDescriptor::Disabled)
+ return Value::fromBoolean(false);
+ }
+ return Value::fromBoolean(true);
+}
+
+Value ObjectPrototype::method_isFrozen(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ if (o->extensible)
+ return Value::fromBoolean(false);
+
+ ObjectIterator it(ctx, o, ObjectIterator::NoFlags);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *pd = it.next(&name, &index);
+ if (!pd)
+ break;
+ if (pd->isWritable() || pd->isConfigurable())
+ return Value::fromBoolean(false);
+ }
+ return Value::fromBoolean(true);
+}
+
+Value ObjectPrototype::method_isExtensible(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+ return Value::fromBoolean(o->extensible);
+}
+
+Value ObjectPrototype::method_keys(ExecutionContext *ctx)
+{
+ if (!ctx->argument(0).isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->argument(0).objectValue();
+
+ ArrayObject *a = ctx->engine->newArrayObject(ctx);
+
+ ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly);
+ while (1) {
+ uint index;
+ String *name;
+ PropertyDescriptor *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->array.push_back(key);
+ }
+
+ return Value::fromObject(a);
+}
+
+Value ObjectPrototype::method_toString(ExecutionContext *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->thisObject, ctx);
+ QString className = obj.objectValue()->className();
+ return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className));
+ }
+}
+
+Value ObjectPrototype::method_toLocaleString(ExecutionContext *ctx)
+{
+ Object *o = __qmljs_to_object(ctx->thisObject, ctx).objectValue();
+ Value ts = o->__get__(ctx, ctx->engine->identifier(QStringLiteral("toString")));
+ FunctionObject *f = ts.asFunctionObject();
+ if (!f)
+ __qmljs_throw_type_error(ctx);
+ return f->call(ctx, Value::fromObject(o), 0, 0);
+}
+
+Value ObjectPrototype::method_valueOf(ExecutionContext *ctx)
+{
+ return ctx->thisObject.toObject(ctx);
+}
+
+Value ObjectPrototype::method_hasOwnProperty(ExecutionContext *ctx)
+{
+ String *P = ctx->argument(0).toString(ctx);
+ Value O = ctx->thisObject.toObject(ctx);
+ bool r = O.objectValue()->__getOwnProperty__(ctx, P) != 0;
+ return Value::fromBoolean(r);
+}
+
+Value ObjectPrototype::method_isPrototypeOf(ExecutionContext *ctx)
+{
+ Value V = ctx->argument(0);
+ if (! V.isObject())
+ return Value::fromBoolean(false);
+
+ Object *O = ctx->thisObject.toObject(ctx).objectValue();
+ 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(ExecutionContext *ctx)
+{
+ String *p = ctx->argument(0).toString(ctx);
+
+ Object *o = ctx->thisObject.toObject(ctx).objectValue();
+ PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p);
+ return Value::fromBoolean(pd && pd->isEnumerable());
+}
+
+Value ObjectPrototype::method_defineGetter(ExecutionContext *ctx)
+{
+ if (ctx->argumentCount < 2)
+ __qmljs_throw_type_error(ctx);
+ String *prop = ctx->argument(0).toString(ctx);
+
+ FunctionObject *f = ctx->argument(1).asFunctionObject();
+ if (!f)
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->thisObject.toObject(ctx).objectValue();
+
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0);
+ pd.configurable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ o->__defineOwnProperty__(ctx, prop, &pd);
+ return Value::undefinedValue();
+}
+
+Value ObjectPrototype::method_defineSetter(ExecutionContext *ctx)
+{
+ if (ctx->argumentCount < 2)
+ __qmljs_throw_type_error(ctx);
+ String *prop = ctx->argument(0).toString(ctx);
+
+ FunctionObject *f = ctx->argument(1).asFunctionObject();
+ if (!f)
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = ctx->thisObject.toObject(ctx).objectValue();
+
+ PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f);
+ pd.configurable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ o->__defineOwnProperty__(ctx, prop, &pd);
+ return Value::undefinedValue();
+}
+
+void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc)
+{
+ if (!v.isObject())
+ __qmljs_throw_type_error(ctx);
+
+ Object *o = v.objectValue();
+
+ desc->type = PropertyDescriptor::Generic;
+
+ desc->enumberable = PropertyDescriptor::Undefined;
+ if (o->__hasProperty__(ctx, ctx->engine->id_enumerable))
+ desc->enumberable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_enumerable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled;
+
+ desc->configurable = PropertyDescriptor::Undefined;
+ if (o->__hasProperty__(ctx, ctx->engine->id_configurable))
+ desc->configurable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_configurable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled;
+
+ desc->get = 0;
+ if (o->__hasProperty__(ctx, ctx->engine->id_get)) {
+ Value get = o->__get__(ctx, ctx->engine->id_get);
+ FunctionObject *f = get.asFunctionObject();
+ if (f) {
+ desc->get = f;
+ } else if (get.isUndefined()) {
+ desc->get = (FunctionObject *)0x1;
+ } else {
+ __qmljs_throw_type_error(ctx);
+ }
+ desc->type = PropertyDescriptor::Accessor;
+ }
+
+ desc->set = 0;
+ if (o->__hasProperty__(ctx, ctx->engine->id_set)) {
+ Value set = o->__get__(ctx, ctx->engine->id_set);
+ FunctionObject *f = set.asFunctionObject();
+ if (f) {
+ desc->set = f;
+ } else if (set.isUndefined()) {
+ desc->set = (FunctionObject *)0x1;
+ } else {
+ __qmljs_throw_type_error(ctx);
+ }
+ desc->type = PropertyDescriptor::Accessor;
+ }
+
+ desc->writable = PropertyDescriptor::Undefined;
+ if (o->__hasProperty__(ctx, ctx->engine->id_writable)) {
+ if (desc->isAccessor())
+ __qmljs_throw_type_error(ctx);
+ desc->writable = __qmljs_to_boolean(o->__get__(ctx, ctx->engine->id_writable), ctx) ? PropertyDescriptor::Enabled : PropertyDescriptor::Disabled;
+ // writable forces it to be a data descriptor
+ desc->value = Value::undefinedValue();
+ }
+
+ if (o->__hasProperty__(ctx, ctx->engine->id_value)) {
+ if (desc->isAccessor())
+ __qmljs_throw_type_error(ctx);
+ desc->value = o->__get__(ctx, ctx->engine->id_value);
+ desc->type = PropertyDescriptor::Data;
+ }
+
+}
+
+
+Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc)
+{
+ 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();
+
+ PropertyDescriptor pd;
+ pd.type = PropertyDescriptor::Data;
+ pd.writable = PropertyDescriptor::Enabled;
+ pd.enumberable = PropertyDescriptor::Enabled;
+ pd.configurable = PropertyDescriptor::Enabled;
+
+ if (desc->isData()) {
+ pd.value = desc->value;
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("value")), &pd);
+ pd.value = Value::fromBoolean(desc->writable == PropertyDescriptor::Enabled ? true : false);
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("writable")), &pd);
+ } else {
+ pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue();
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("get")), &pd);
+ pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue();
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("set")), &pd);
+ }
+ pd.value = Value::fromBoolean(desc->enumberable == PropertyDescriptor::Enabled ? true : false);
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("enumerable")), &pd);
+ pd.value = Value::fromBoolean(desc->configurable == PropertyDescriptor::Enabled ? true : false);
+ o->__defineOwnProperty__(ctx, engine->identifier(QStringLiteral("configurable")), &pd);
+
+ return Value::fromObject(o);
+}
diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h
new file mode 100644
index 0000000000..93527a28ef
--- /dev/null
+++ b/src/v4/qv4objectproto.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct ObjectCtor: FunctionObject
+{
+ ObjectCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct ObjectPrototype: Object
+{
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_getPrototypeOf(ExecutionContext *ctx);
+ static Value method_getOwnPropertyDescriptor(ExecutionContext *ctx);
+ static Value method_getOwnPropertyNames(ExecutionContext *ctx);
+ static Value method_create(ExecutionContext *ctx);
+ static Value method_defineProperty(ExecutionContext *ctx);
+ static Value method_defineProperties(ExecutionContext *ctx);
+ static Value method_seal(ExecutionContext *ctx);
+ static Value method_freeze(ExecutionContext *ctx);
+ static Value method_preventExtensions(ExecutionContext *ctx);
+ static Value method_isSealed(ExecutionContext *ctx);
+ static Value method_isFrozen(ExecutionContext *ctx);
+ static Value method_isExtensible(ExecutionContext *ctx);
+ static Value method_keys(ExecutionContext *ctx);
+
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_toLocaleString(ExecutionContext *ctx);
+ static Value method_valueOf(ExecutionContext *ctx);
+ static Value method_hasOwnProperty(ExecutionContext *ctx);
+ static Value method_isPrototypeOf(ExecutionContext *ctx);
+ static Value method_propertyIsEnumerable(ExecutionContext *ctx);
+
+ static Value method_defineGetter(ExecutionContext *ctx);
+ static Value method_defineSetter(ExecutionContext *ctx);
+
+ static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc);
+ static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc);
+};
+
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4propertydescriptor.h b/src/v4/qv4propertydescriptor.h
new file mode 100644
index 0000000000..dc9e1c556d
--- /dev/null
+++ b/src/v4/qv4propertydescriptor.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 QV4PROPERTYDESCRIPTOR_H
+#define QV4PROPERTYDESCRIPTOR_H
+
+#include "qmljs_value.h"
+
+namespace QQmlJS {
+namespace VM {
+
+struct FunctionObject;
+
+struct PropertyDescriptor {
+ enum Type {
+ Generic,
+ Data,
+ Accessor
+ };
+ enum State {
+ Undefined,
+ Disabled,
+ Enabled
+ };
+ union {
+ Value value;
+ struct {
+ FunctionObject *get;
+ FunctionObject *set;
+ };
+ };
+ uint type : 8;
+ uint writable : 8;
+ uint enumberable : 8;
+ uint configurable : 8;
+
+ static inline PropertyDescriptor fromValue(Value v) {
+ PropertyDescriptor pd;
+ pd.value = v;
+ pd.type = Data;
+ pd.writable = Undefined;
+ pd.enumberable = Undefined;
+ pd.configurable = Undefined;
+ return pd;
+ }
+ static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) {
+ PropertyDescriptor pd;
+ pd.get = getter;
+ pd.set = setter;
+ pd.type = Accessor;
+ pd.writable = Undefined;
+ pd.enumberable = Undefined;
+ pd.configurable = Undefined;
+ return pd;
+ }
+
+ // Section 8.10
+ inline void fullyPopulated() {
+ if (type == Generic) {
+ type = Data;
+ value = Value::undefinedValue();
+ }
+ if (type == Data) {
+ if (writable == Undefined)
+ writable = Disabled;
+ } else {
+ writable = Undefined;
+ if ((quintptr)get == 0x1)
+ get = 0;
+ if ((quintptr)set == 0x1)
+ set = 0;
+ }
+ if (enumberable == Undefined)
+ enumberable = Disabled;
+ if (configurable == Undefined)
+ configurable = Disabled;
+ }
+
+ inline bool isData() const { return type == Data || writable != Undefined; }
+ inline bool isAccessor() const { return type == Accessor; }
+ inline bool isGeneric() const { return type == Generic && writable == Undefined; }
+
+ inline bool isWritable() const { return writable == Enabled; }
+ inline bool isEnumerable() const { return enumberable == Enabled; }
+ inline bool isConfigurable() const { return configurable == Enabled; }
+
+ inline bool isEmpty() const {
+ return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined;
+ }
+ inline bool isSubset(PropertyDescriptor *other) const {
+ if (type != Generic && type != other->type)
+ return false;
+ if (enumberable != Undefined && enumberable != other->enumberable)
+ return false;
+ if (configurable != Undefined && configurable != other->configurable)
+ return false;
+ if (writable != Undefined && writable != other->writable)
+ return false;
+ if (type == Data && !value.sameValue(other->value))
+ return false;
+ if (type == Accessor) {
+ if (get != other->get)
+ return false;
+ if (set != other->set)
+ return false;
+ }
+ return true;
+ }
+ inline void operator+=(const PropertyDescriptor &other) {
+ if (other.enumberable != Undefined)
+ enumberable = other.enumberable;
+ if (other.configurable != Undefined)
+ configurable = other.configurable;
+ if (other.writable != Undefined)
+ writable = other.writable;
+ if (other.type == Accessor) {
+ type = Accessor;
+ if (other.get)
+ get = ((quintptr)other.get == 0x1) ? 0 : other.get;
+ if (other.set)
+ set = ((quintptr)other.set == 0x1) ? 0 : other.set;
+ } else if (other.type == Data){
+ type = Data;
+ value = other.value;
+ }
+ }
+};
+
+}
+}
+
+#endif
diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h
new file mode 100644
index 0000000000..3bd47679b5
--- /dev/null
+++ b/src/v4/qv4propertytable.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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 QV4PROPERTYTABLE_H
+#define QV4PROPERTYTABLE_H
+
+#include "qmljs_value.h"
+#include "qv4propertydescriptor.h"
+
+namespace QQmlJS {
+namespace VM {
+
+struct ObjectIterator;
+
+struct PropertyTableEntry {
+ PropertyDescriptor descriptor;
+ String *name;
+ PropertyTableEntry *next;
+ int index;
+
+ inline PropertyTableEntry(String *name)
+ : name(name),
+ next(0),
+ index(-1)
+ { }
+
+ inline bool hasName(String *n) const { return name->isEqualTo(n); }
+ inline unsigned hashValue() const { return name->hashValue(); }
+};
+
+class PropertyTable
+{
+ Q_DISABLE_COPY(PropertyTable)
+
+public:
+ PropertyTable()
+ : _properties(0)
+ , _buckets(0)
+ , _freeList(0)
+ , _propertyCount(0)
+ , _bucketCount(0)
+ , _primeIdx(-1)
+ , _allocated(0)
+ {}
+
+ ~PropertyTable()
+ {
+ qDeleteAll(_properties, _properties + _propertyCount);
+ delete[] _properties;
+ delete[] _buckets;
+ }
+
+ typedef PropertyTableEntry **iterator;
+ inline iterator begin() const { return _properties; }
+ inline iterator end() const { return _properties + _propertyCount; }
+
+ void remove(PropertyTableEntry *prop)
+ {
+ PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount);
+ if (*bucket == prop) {
+ *bucket = prop->next;
+ } else {
+ for (PropertyTableEntry *it = *bucket; it; it = it->next) {
+ if (it->next == prop) {
+ it->next = it->next->next;
+ break;
+ }
+ }
+ }
+
+ _properties[prop->index] = 0;
+ prop->next = _freeList;
+ _freeList = prop;
+ }
+
+ PropertyTableEntry *findEntry(String *name) const
+ {
+ if (_properties) {
+ for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
+ if (prop && (prop->name == name || prop->hasName(name)))
+ return prop;
+ }
+ }
+
+ return 0;
+ }
+
+ PropertyDescriptor *find(String *name) const
+ {
+ if (_properties) {
+ for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
+ if (prop && (prop->name == name || prop->hasName(name)))
+ return &prop->descriptor;
+ }
+ }
+
+ return 0;
+ }
+
+ PropertyDescriptor *insert(String *name)
+ {
+ if (PropertyTableEntry *prop = findEntry(name))
+ return &prop->descriptor;
+
+ if (_propertyCount == _allocated) {
+ if (! _allocated)
+ _allocated = 4;
+ else
+ _allocated *= 2;
+
+ PropertyTableEntry **properties = new PropertyTableEntry*[_allocated];
+ std::copy(_properties, _properties + _propertyCount, properties);
+ delete[] _properties;
+ _properties = properties;
+ }
+
+ PropertyTableEntry *prop;
+ if (_freeList) {
+ prop = _freeList;
+ prop->name = name;
+ _freeList = _freeList->next;
+ } else {
+ prop = new PropertyTableEntry(name);
+ }
+
+ prop->index = _propertyCount;
+ _properties[_propertyCount] = prop;
+ ++_propertyCount;
+
+ if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) {
+ rehash();
+ } else {
+ PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
+ prop->next = bucket;
+ bucket = prop;
+ }
+
+ return &prop->descriptor;
+ }
+
+private:
+ void rehash()
+ {
+ _bucketCount = nextPrime();
+
+ delete[] _buckets;
+ _buckets = new PropertyTableEntry *[_bucketCount];
+ std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0);
+
+ for (int i = 0; i < _propertyCount; ++i) {
+ PropertyTableEntry *prop = _properties[i];
+ if (prop) {
+ PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
+ prop->next = bucket;
+ bucket = prop;
+ }
+ }
+ }
+
+ inline int nextPrime()
+ {
+ // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits!
+ static const int primes[] = {
+ 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877
+ };
+
+ if (_primeIdx < (int) (sizeof(primes)/sizeof(int)))
+ return primes[++_primeIdx];
+ else
+ return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties?
+ }
+
+private:
+ friend struct ObjectIterator;
+ PropertyTableEntry **_properties;
+ PropertyTableEntry **_buckets;
+ PropertyTableEntry *_freeList;
+ int _propertyCount;
+ int _bucketCount;
+ int _primeIdx: 5;
+ int _allocated: 27;
+};
+
+}
+}
+
+#endif
diff --git a/src/v4/qv4regexp.cpp b/src/v4/qv4regexp.cpp
new file mode 100644
index 0000000000..bcef815e25
--- /dev/null
+++ b/src/v4/qv4regexp.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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 "qmljs_engine.h"
+
+namespace QQmlJS {
+namespace VM {
+
+uint RegExp::match(const QString &string, int start, uint *matchOffsets)
+{
+ if (!isValid())
+ return JSC::Yarr::offsetNoMatch;
+
+ return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).characters16(), string.length(), start, matchOffsets);
+}
+
+RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline)
+ : m_pattern(pattern)
+ , m_subPatternCount(0)
+ , m_ignoreCase(ignoreCase)
+ , m_multiLine(multiline)
+{
+ 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);
+}
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+
diff --git a/src/v4/qv4regexp.h b/src/v4/qv4regexp.h
new file mode 100644
index 0000000000..7eae033bec
--- /dev/null
+++ b/src/v4/qv4regexp.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/FastAllocBase.h>
+#include <wtf/BumpPointerAllocator.h>
+
+#include <limits.h>
+
+#include <yarr/Yarr.h>
+#include <yarr/YarrInterpreter.h>
+
+namespace QQmlJS {
+namespace VM {
+
+struct ExecutionEngine;
+
+class RegExp : public RefCounted<RegExp>
+{
+public:
+ static PassRefPtr<RegExp> create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false)
+ { return adoptRef(new RegExp(engine, pattern, ignoreCase, multiline)); }
+
+ QString pattern() const { return m_pattern; }
+
+ bool isValid() const { return m_byteCode.get(); }
+
+ uint match(const QString& string, int start, uint *matchOffsets);
+
+ bool ignoreCase() const { return m_ignoreCase; }
+ bool multiLine() const { return m_multiLine; }
+ int captureCount() const { return m_subPatternCount + 1; }
+
+private:
+ Q_DISABLE_COPY(RegExp);
+ RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline);
+
+ const QString m_pattern;
+ OwnPtr<JSC::Yarr::BytecodePattern> m_byteCode;
+ int m_subPatternCount;
+ const bool m_ignoreCase;
+ const bool m_multiLine;
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4REGEXP_H
diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp
new file mode 100644
index 0000000000..1f78c56782
--- /dev/null
+++ b/src/v4/qv4regexpobject.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 "qv4regexpobject.h"
+#include "qv4ir_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 <qv4ir_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 <alloca.h>
+
+using namespace QQmlJS::VM;
+
+
+RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr<RegExp> value, bool global)
+ : value(value)
+ , global(global)
+{
+ type = Type_RegExpObject;
+
+ if (!members)
+ members.reset(new PropertyTable());
+ lastIndexProperty = members->insert(engine->identifier(QStringLiteral("lastIndex")));
+ lastIndexProperty->type = PropertyDescriptor::Data;
+ lastIndexProperty->writable = PropertyDescriptor::Enabled;
+ lastIndexProperty->enumberable = PropertyDescriptor::Disabled;
+ lastIndexProperty->configurable = PropertyDescriptor::Disabled;
+ lastIndexProperty->value = Value::fromInt32(0);
+ if (!this->value.get())
+ return;
+ defineReadonlyProperty(engine->identifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern())));
+ defineReadonlyProperty(engine->identifier(QStringLiteral("global")), Value::fromBoolean(global));
+ defineReadonlyProperty(engine->identifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase()));
+ defineReadonlyProperty(engine->identifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine()));
+}
+
+
+RegExpCtor::RegExpCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value RegExpCtor::construct(ExecutionContext *ctx)
+{
+ Value r = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue();
+ Value f = ctx->argumentCount > 1 ? ctx->argument(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);
+ }
+ }
+ }
+
+ RefPtr<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(ExecutionContext *ctx)
+{
+ if (ctx->argumentCount > 0 && ctx->argument(0).asRegExpObject()) {
+ if (ctx->argumentCount == 1 || ctx->argument(1).isUndefined())
+ return ctx->argument(0);
+ }
+
+ return construct(ctx);
+}
+
+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(ExecutionContext *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->value.toInt32(ctx) : 0;
+ if (offset < 0 || offset > s.length()) {
+ r->lastIndexProperty->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->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->array.push_back(entry);
+ }
+
+ array->__put__(ctx, QLatin1String("index"), Value::fromInt32(result));
+ array->__put__(ctx, QLatin1String("input"), arg);
+
+ if (r->global)
+ r->lastIndexProperty->value = Value::fromInt32(matchOffsets[1]);
+
+ return Value::fromObject(array);
+}
+
+Value RegExpPrototype::method_test(ExecutionContext *ctx)
+{
+ Value r = method_exec(ctx);
+ return Value::fromBoolean(!r.isNull());
+}
+
+Value RegExpPrototype::method_toString(ExecutionContext *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(ExecutionContext *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/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h
new file mode 100644
index 0000000000..50241f2daf
--- /dev/null
+++ b/src/v4/qv4regexpobject.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include "qmljs_engine.h"
+#include "qmljs_environment.h"
+#include "qv4functionobject.h"
+#include "qv4array.h"
+#include "qv4string.h"
+#include "qv4codegen_p.h"
+#include "qv4isel_p.h"
+#include "qv4managed.h"
+#include "qv4propertydescriptor.h"
+#include "qv4propertytable.h"
+#include "qv4objectiterator.h"
+#include "qv4regexp.h"
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QScopedPointer>
+#include <cstdio>
+#include <cassert>
+
+namespace QQmlJS {
+
+namespace VM {
+
+struct RegExpObject: Object {
+ RefPtr<RegExp> value;
+ PropertyDescriptor *lastIndexProperty;
+ bool global;
+ RegExpObject(ExecutionEngine *engine, PassRefPtr<RegExp> value, bool global);
+};
+
+
+struct RegExpCtor: FunctionObject
+{
+ RegExpCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct RegExpPrototype: RegExpObject
+{
+ RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(0, QString()), false) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_exec(ExecutionContext *ctx);
+ static Value method_test(ExecutionContext *ctx);
+ static Value method_toString(ExecutionContext *ctx);
+ static Value method_compile(ExecutionContext *ctx);
+};
+
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+#endif // QMLJS_OBJECTS_H
diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp
new file mode 100644
index 0000000000..6e0c14d108
--- /dev/null
+++ b/src/v4/qv4string.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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 "qmljs_runtime.h"
+#include <QtCore/QHash>
+
+namespace QQmlJS {
+namespace VM {
+
+static uint toArrayIndex(const QChar *ch, const QChar *end)
+{
+ uint i = ch->unicode() - '0';
+ if (i > 9)
+ return String::InvalidArrayIndex;
+ ++ch;
+ // reject "01", "001", ...
+ if (i == 0 && ch != end)
+ return String::InvalidArrayIndex;
+
+ while (ch < end) {
+ uint x = ch->unicode() - '0';
+ if (x > 9)
+ return String::InvalidArrayIndex;
+ uint n = i*10 + x;
+ if (n < i)
+ // overflow
+ return String::InvalidArrayIndex;
+ i = n;
+ ++ch;
+ }
+ return i;
+}
+
+uint String::asArrayIndexSlow() const
+{
+ if (stringHash < LargestHashedArrayIndex)
+ return stringHash;
+
+ const QChar *ch = _text.constData();
+ const QChar *end = ch + _text.length();
+ return toArrayIndex(ch, end);
+}
+
+uint String::toUInt(bool *ok) const
+{
+ *ok = true;
+
+ if (stringHash == InvalidHashValue)
+ createHashValue();
+ if (stringHash < LargestHashedArrayIndex)
+ return stringHash;
+
+ double d = __qmljs_string_to_number(this);
+ uint l = (uint)d;
+ if (d == l)
+ return l;
+ *ok = false;
+ return UINT_MAX;
+}
+
+void String::createHashValue() const
+{
+ const QChar *ch = _text.constData();
+ const QChar *end = ch + _text.length();
+
+ // array indices get their number as hash value, for large numbers we set to INT_MAX
+ stringHash = toArrayIndex(ch, end);
+ if (stringHash < UINT_MAX) {
+ if (stringHash > INT_MAX)
+ stringHash = INT_MAX;
+ return;
+ }
+
+ uint h = 0xffffffff;
+ while (ch < end) {
+ h = 31 * h + ch->unicode();
+ ++ch;
+ }
+
+ // set highest bit to mark it as a non number
+ stringHash = h | 0xf0000000;
+}
+
+}
+}
diff --git a/src/v4/qv4string.h b/src/v4/qv4string.h
new file mode 100644
index 0000000000..34e241218c
--- /dev/null
+++ b/src/v4/qv4string.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct String : public Managed {
+ String(const QString &text)
+ : _text(text) { type = Type_String; stringHash = InvalidHashValue; }
+
+ inline bool isEqualTo(const String *other) const {
+ if (this == other)
+ return true;
+ else if (other && hashValue() == other->hashValue())
+ return toQString() == other->toQString();
+ return false;
+ }
+
+ inline const QString &toQString() const {
+ return _text;
+ }
+
+ inline unsigned hashValue() const {
+ if (stringHash == InvalidHashValue)
+ createHashValue();
+
+ return stringHash;
+ }
+ enum {
+ InvalidArrayIndex = 0xffffffff,
+ LargestHashedArrayIndex = 0x7fffffff,
+ InvalidHashValue = 0xffffffff
+ };
+ uint asArrayIndex() const {
+ if (stringHash == InvalidHashValue)
+ createHashValue();
+ if (stringHash > LargestHashedArrayIndex)
+ return InvalidArrayIndex;
+ if (stringHash < LargestHashedArrayIndex)
+ return stringHash;
+ return asArrayIndexSlow();
+ }
+ uint asArrayIndexSlow() const;
+ uint toUInt(bool *ok) const;
+
+private:
+ void createHashValue() const;
+
+ QString _text;
+};
+
+}
+}
+
+#endif
diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp
new file mode 100644
index 0000000000..81968cc55a
--- /dev/null
+++ b/src/v4/qv4stringobject.cpp
@@ -0,0 +1,722 @@
+/****************************************************************************
+**
+** 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 <qv4ir_p.h>
+#include <qv4codegen_p.h>
+#include <qv4isel_masm_p.h>
+
+#ifndef Q_WS_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;
+
+StringObject::StringObject(ExecutionContext *ctx, const Value &value)
+ : value(value)
+{
+ type = Type_StringObject;
+
+ tmpProperty.type = PropertyDescriptor::Data;
+ tmpProperty.enumberable = PropertyDescriptor::Enabled;
+ tmpProperty.writable = PropertyDescriptor::Disabled;
+ tmpProperty.configurable = PropertyDescriptor::Disabled;
+ tmpProperty.value = Value::undefinedValue();
+
+ assert(value.isString());
+ defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length()));
+}
+
+PropertyDescriptor *StringObject::getIndex(ExecutionContext *ctx, uint index)
+{
+ QString str = value.stringValue()->toQString();
+ if (index >= (uint)str.length())
+ return 0;
+ String *result = ctx->engine->newString(str.mid(index, 1));
+ tmpProperty.value = Value::fromString(result);
+ return &tmpProperty;
+}
+
+void StringObject::markObjects()
+{
+ value.stringValue()->mark();
+ Object::markObjects();
+}
+
+
+StringCtor::StringCtor(ExecutionContext *scope)
+ : FunctionObject(scope)
+{
+}
+
+Value StringCtor::construct(ExecutionContext *ctx)
+{
+ Value value;
+ if (ctx->argumentCount)
+ value = Value::fromString(ctx->argument(0).toString(ctx));
+ else
+ value = Value::fromString(ctx, QString());
+ return Value::fromObject(ctx->engine->newStringObject(ctx, value));
+}
+
+Value StringCtor::call(ExecutionContext *ctx)
+{
+ Value value;
+ if (ctx->argumentCount)
+ value = Value::fromString(ctx->argument(0).toString(ctx));
+ else
+ value = Value::fromString(ctx, 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 *parentCtx, 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())
+ parentCtx->throwTypeError();
+ else
+ str = thisObject.toString(parentCtx);
+ return str->toQString();
+}
+
+Value StringPrototype::method_toString(ExecutionContext *parentCtx, Value thisObject, Value *, int)
+{
+ if (thisObject.isString())
+ return thisObject;
+
+ StringObject *o = thisObject.asStringObject();
+ if (!o)
+ parentCtx->throwTypeError();
+ return o->value;
+}
+
+Value StringPrototype::method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ const QString str = getThisString(parentCtx, thisObject);
+
+ int pos = 0;
+ if (argc > 0)
+ pos = (int) argv[0].toInteger(parentCtx);
+
+ QString result;
+ if (pos >= 0 && pos < str.length())
+ result += str.at(pos);
+
+ return Value::fromString(parentCtx, result);
+}
+
+Value StringPrototype::method_charCodeAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ const QString str = getThisString(parentCtx, thisObject);
+
+ int pos = 0;
+ if (argc > 0)
+ pos = (int) argv[0].toInteger(parentCtx);
+
+
+ if (pos >= 0 && pos < str.length())
+ return Value::fromInt32(str.at(pos).unicode());
+
+ return Value::fromDouble(qSNaN());
+}
+
+Value StringPrototype::method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ QString value = getThisString(parentCtx, thisObject);
+
+ for (int i = 0; i < argc; ++i) {
+ Value v = __qmljs_to_string(argv[i], parentCtx);
+ assert(v.isString());
+ value += v.stringValue()->toQString();
+ }
+
+ return Value::fromString(parentCtx, value);
+}
+
+Value StringPrototype::method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ QString value = getThisString(parentCtx, thisObject);
+
+ QString searchString;
+ if (argc)
+ searchString = argv[0].toString(parentCtx)->toQString();
+
+ int pos = 0;
+ if (argc > 1)
+ pos = (int) argv[1].toInteger(parentCtx);
+
+ int index = -1;
+ if (! value.isEmpty())
+ index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length()));
+
+ return Value::fromDouble(index);
+}
+
+Value StringPrototype::method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ const QString value = getThisString(parentCtx, thisObject);
+
+ QString searchString;
+ if (argc) {
+ Value v = __qmljs_to_string(argv[0], parentCtx);
+ searchString = v.stringValue()->toQString();
+ }
+
+ Value posArg = argc > 1 ? argv[1] : Value::undefinedValue();
+ double position = __qmljs_to_number(posArg, parentCtx);
+ if (std::isnan(position))
+ position = +qInf();
+ else
+ position = trunc(position);
+
+ int pos = trunc(qMin(qMax(position, 0.0), double(value.length())));
+ if (!searchString.isEmpty() && pos == value.length())
+ --pos;
+ int index = value.lastIndexOf(searchString, pos);
+ return Value::fromDouble(index);
+}
+
+Value StringPrototype::method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ const QString value = getThisString(parentCtx, thisObject);
+ const QString that = (argc ? argv[0] : Value::undefinedValue()).toString(parentCtx)->toQString();
+ return Value::fromDouble(QString::localeAwareCompare(value, that));
+}
+
+Value StringPrototype::method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ if (thisObject.isUndefined() || thisObject.isNull())
+ __qmljs_throw_type_error(parentCtx);
+
+ String *s = thisObject.toString(parentCtx);
+
+ Value regexp = argc ? argv[0] : Value::undefinedValue();
+ RegExpObject *rx = regexp.asRegExpObject();
+ if (!rx)
+ rx = parentCtx->engine->regExpCtor.asFunctionObject()->construct(parentCtx, &regexp, 1).asRegExpObject();
+
+ if (!rx)
+ // ### CHECK
+ __qmljs_throw_type_error(parentCtx);
+
+ bool global = rx->global;
+
+ // ### use the standard builtin function, not the one that might be redefined in the proto
+ FunctionObject *exec = parentCtx->engine->regExpPrototype->__get__(parentCtx, parentCtx->engine->identifier(QStringLiteral("exec")), 0).asFunctionObject();
+
+ Value arg = Value::fromString(s);
+ if (!global)
+ return exec->call(parentCtx, Value::fromObject(rx), &arg, 1);
+
+ String *lastIndex = parentCtx->engine->identifier(QStringLiteral("lastIndex"));
+ rx->__put__(parentCtx, lastIndex, Value::fromInt32(0));
+ ArrayObject *a = parentCtx->engine->newArrayObject(parentCtx);
+
+ double previousLastIndex = 0;
+ uint n = 0;
+ while (1) {
+ Value result = exec->call(parentCtx, Value::fromObject(rx), &arg, 1);
+ if (result.isNull())
+ break;
+ assert(result.isObject());
+ double thisIndex = rx->__get__(parentCtx, lastIndex, 0).toInteger(parentCtx);
+ if (previousLastIndex == thisIndex) {
+ previousLastIndex = thisIndex + 1;
+ rx->__put__(parentCtx, lastIndex, Value::fromDouble(previousLastIndex));
+ } else {
+ previousLastIndex = thisIndex;
+ }
+ Value matchStr = result.objectValue()->__get__(parentCtx, (uint)0, (bool *)0);
+ a->array.set(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(ExecutionContext *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->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(ExecutionContext *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(ExecutionContext *ctx)
+{
+ const QString text = getThisString(ctx);
+ const double length = text.length();
+
+ double start = ctx->argument(0).toInteger(ctx);
+ double end = ctx->argument(1).isUndefined()
+ ? length : ctx->argument(1).toInteger(ctx);
+
+ 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(ExecutionContext *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->array.push_back(Value::fromString(ctx, text));
+ return result;
+ }
+ return Value::fromString(ctx, text.left(limitValue.toInteger(ctx)));
+ }
+
+ uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(ctx);
+
+ 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->array.push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset)));
+ offset = qMax(offset + 1, matchOffsets[1]);
+
+ if (array->array.length() >= limit)
+ break;
+
+ for (int i = 1; i < re->value->captureCount(); ++i) {
+ uint start = matchOffsets[i * 2];
+ uint end = matchOffsets[i * 2 + 1];
+ array->array.push_back(Value::fromString(ctx, text.mid(start, end - start)));
+ if (array->array.length() >= limit)
+ break;
+ }
+ }
+ if (array->array.length() < limit)
+ array->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->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->array.push_back(Value::fromString(ctx, text.mid(start, end - start)));
+ start = end + separator.size();
+ if (array->array.length() >= limit)
+ break;
+ }
+ if (array->array.length() < limit && start != -1)
+ array->array.push_back(Value::fromString(ctx, text.mid(start)));
+ }
+ return result;
+}
+
+Value StringPrototype::method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ const QString value = getThisString(parentCtx, thisObject);
+
+ double start = 0;
+ if (argc > 0)
+ start = argv[0].toInteger(parentCtx);
+
+ double length = +qInf();
+ if (argc > 1)
+ length = argv[1].toInteger(parentCtx);
+
+ 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(parentCtx, value.mid(x, y));
+}
+
+Value StringPrototype::method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc)
+{
+ QString value = getThisString(parentCtx, thisObject);
+ int length = value.length();
+
+ double start = 0;
+ double end = length;
+
+ if (argc > 0)
+ start = argv[0].toInteger(parentCtx);
+
+ Value endValue = argc > 1 ? argv[1] : Value::undefinedValue();
+ if (!endValue.isUndefined())
+ end = endValue.toInteger(parentCtx);
+
+ if (std::isnan(start) || start < 0)
+ start = 0;
+
+ if (std::isnan(end) || end < 0)
+ end = 0;
+
+ if (start > length)
+ start = length;
+
+ if (end > length)
+ end = length;
+
+ if (start > end) {
+ double was = start;
+ start = end;
+ end = was;
+ }
+
+ qint32 x = (int)start;
+ qint32 y = (int)(end - start);
+ return Value::fromString(parentCtx, value.mid(x, y));
+}
+
+Value StringPrototype::method_toLowerCase(ExecutionContext *ctx)
+{
+ QString value = getThisString(ctx);
+ return Value::fromString(ctx, value.toLower());
+}
+
+Value StringPrototype::method_toLocaleLowerCase(ExecutionContext *ctx)
+{
+ return method_toLowerCase(ctx);
+}
+
+Value StringPrototype::method_toUpperCase(ExecutionContext *ctx)
+{
+ QString value = getThisString(ctx);
+ return Value::fromString(ctx, value.toUpper());
+}
+
+Value StringPrototype::method_toLocaleUpperCase(ExecutionContext *ctx)
+{
+ return method_toUpperCase(ctx);
+}
+
+Value StringPrototype::method_fromCharCode(ExecutionContext *parentCtx, Value, Value *argv, int argc)
+{
+ QString str(argc, Qt::Uninitialized);
+ QChar *ch = str.data();
+ for (int i = 0; i < argc; ++i) {
+ *ch = QChar(argv[i].toUInt16(parentCtx));
+ ++ch;
+ }
+ return Value::fromString(parentCtx, str);
+}
+
+Value StringPrototype::method_trim(ExecutionContext *ctx)
+{
+ if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined())
+ __qmljs_throw_type_error(ctx);
+
+ 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/v4/qv4stringobject.h b/src/v4/qv4stringobject.h
new file mode 100644
index 0000000000..1becd97a46
--- /dev/null
+++ b/src/v4/qv4stringobject.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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>
+
+namespace QQmlJS {
+namespace VM {
+
+struct StringObject: Object {
+ Value value;
+ PropertyDescriptor tmpProperty;
+ StringObject(ExecutionContext *ctx, const Value &value);
+
+ PropertyDescriptor *getIndex(ExecutionContext *ctx, uint index);
+
+protected:
+ virtual void markObjects();
+};
+
+struct StringCtor: FunctionObject
+{
+ StringCtor(ExecutionContext *scope);
+
+ virtual Value construct(ExecutionContext *ctx);
+ virtual Value call(ExecutionContext *ctx);
+};
+
+struct StringPrototype: StringObject
+{
+ StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {}
+ void init(ExecutionContext *ctx, const Value &ctor);
+
+ static Value method_toString(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_charAt(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_charCodeAt(ExecutionContext *, Value thisObject, Value *argv, int argc);
+ static Value method_concat(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_indexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_lastIndexOf(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_localeCompare(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_match(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_replace(ExecutionContext *ctx);
+ static Value method_search(ExecutionContext *ctx);
+ static Value method_slice(ExecutionContext *ctx);
+ static Value method_split(ExecutionContext *ctx);
+ static Value method_substr(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_substring(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_toLowerCase(ExecutionContext *ctx);
+ static Value method_toLocaleLowerCase(ExecutionContext *ctx);
+ static Value method_toUpperCase(ExecutionContext *ctx);
+ static Value method_toLocaleUpperCase(ExecutionContext *ctx);
+ static Value method_fromCharCode(ExecutionContext *parentCtx, Value thisObject, Value *argv, int argc);
+ static Value method_trim(ExecutionContext *ctx);
+};
+
+} // end of namespace VM
+} // end of namespace QQmlJS
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/v4/qv4syntaxchecker.cpp b/src/v4/qv4syntaxchecker.cpp
new file mode 100644
index 0000000000..fcda486af2
--- /dev/null
+++ b/src/v4/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/v4/qv4syntaxchecker_p.h b/src/v4/qv4syntaxchecker_p.h
new file mode 100644
index 0000000000..38e123762e
--- /dev/null
+++ b/src/v4/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/v4/v4.pro b/src/v4/v4.pro
new file mode 100644
index 0000000000..6d990ed234
--- /dev/null
+++ b/src/v4/v4.pro
@@ -0,0 +1,155 @@
+TARGET = QtV4
+QT_PRIVATE = core-private qmldevtools-private
+
+CONFIG += internal_module
+
+LLVM_CONFIG=llvm-config
+
+OBJECTS_DIR=.obj
+
+load(qt_build_config)
+load(qt_module)
+
+CONFIG += warn_off
+
+#win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??!
+
+
+# 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
+
+!macx-clang*:LIBS += -rdynamic
+
+SOURCES += \
+ qv4codegen.cpp \
+ qv4ir.cpp \
+ qmljs_engine.cpp \
+ qmljs_environment.cpp \
+ qmljs_runtime.cpp \
+ qmljs_value.cpp \
+ qv4syntaxchecker.cpp \
+ qv4isel_masm.cpp \
+ llvm_runtime.cpp \
+ qv4isel_p.cpp \
+ debugging.cpp \
+ qv4mm.cpp \
+ qv4managed.cpp \
+ qv4array.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
+
+HEADERS += \
+ qv4global.h \
+ qv4codegen_p.h \
+ qv4ir_p.h \
+ qmljs_engine.h \
+ qmljs_environment.h \
+ qmljs_runtime.h \
+ qmljs_math.h \
+ qmljs_value.h \
+ qv4syntaxchecker_p.h \
+ qv4isel_masm_p.h \
+ qv4isel_p.h \
+ qv4isel_util_p.h \
+ debugging.h \
+ qv4mm.h \
+ qv4managed.h \
+ qv4array.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 \
+ qv4propertydescriptor.h \
+ qv4propertytable.h \
+ qv4objectiterator.h \
+ qv4regexp.h
+
+llvm {
+
+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\\\"\""
+
+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 $(INCPATH) $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC
+
+
+} else {
+
+DEFINES += QMLJS_NO_LLVM
+
+}
+
+# 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 = $$OUT_PWD/../tools/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
+
+
+include(moth/moth.pri)
+include(../3rdparty/masm/masm.pri)
+include(../3rdparty/double-conversion/double-conversion.pri)