diff options
Diffstat (limited to 'src/qml/qml/v4vm')
94 files changed, 37750 insertions, 0 deletions
diff --git a/src/qml/qml/v4vm/debugging.cpp b/src/qml/qml/v4vm/debugging.cpp new file mode 100644 index 0000000000..6137e64f89 --- /dev/null +++ b/src/qml/qml/v4vm/debugging.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "debugging.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include <iostream> + +#define LOW_LEVEL_DEBUGGING_HELPERS + +using namespace QQmlJS; +using namespace QQmlJS::Debugging; + +FunctionState::FunctionState(VM::ExecutionContext *context) + : _context(context) +{ + if (debugger()) + debugger()->enterFunction(this); +} + +FunctionState::~FunctionState() +{ + if (debugger()) + debugger()->leaveFunction(this); +} + +VM::Value *FunctionState::argument(unsigned idx) +{ + VM::CallContext *c = _context->asCallContext(); + if (!c || idx >= c->argumentCount) + return 0; + return c->arguments + idx; +} + +VM::Value *FunctionState::local(unsigned idx) +{ + VM::CallContext *c = _context->asCallContext(); + if (c && idx < c->variableCount()) + return c->locals + idx; + return 0; +} + +#ifdef LOW_LEVEL_DEBUGGING_HELPERS +Debugger *globalInstance = 0; + +void printStackTrace() +{ + if (globalInstance) + globalInstance->printStackTrace(); + else + std::cerr << "No debugger." << std::endl; +} +#endif // DO_TRACE_INSTR + +Debugger::Debugger(VM::ExecutionEngine *engine) + : _engine(engine) +{ +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = this; +#endif // DO_TRACE_INSTR +} + +Debugger::~Debugger() +{ +#ifdef LOW_LEVEL_DEBUGGING_HELPERS + globalInstance = 0; +#endif // DO_TRACE_INSTR + + qDeleteAll(_functionInfo.values()); +} + +void Debugger::addFunction(V4IR::Function *function) +{ + _functionInfo.insert(function, new FunctionDebugInfo(function)); +} + +void Debugger::setSourceLocation(V4IR::Function *function, unsigned line, unsigned column) +{ + _functionInfo[function]->setSourceLocation(line, column); +} + +void Debugger::mapFunction(VM::Function *vmf, V4IR::Function *irf) +{ + _vmToIr.insert(vmf, irf); +} + +FunctionDebugInfo *Debugger::debugInfo(VM::FunctionObject *function) const +{ + if (!function) + return 0; + + if (function->function) + return _functionInfo[irFunction(function->function)]; + else + return 0; +} + +QString Debugger::name(VM::FunctionObject *function) const +{ + if (FunctionDebugInfo *i = debugInfo(function)) + return i->name; + + return QString(); +} + +void Debugger::aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context) +{ + _callStack.append(CallInfo(context, function)); +} + +void Debugger::justLeft(VM::ExecutionContext *context) +{ + int idx = callIndex(context); + if (idx < 0) + qDebug() << "Oops, leaving a function that was not registered...?"; + else + _callStack.resize(idx); +} + +void Debugger::enterFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = state; + +#ifdef DO_TRACE_INSTR + QString n = name(_callStack[callIndex(state->context())].function); + std::cerr << "*** Entering \"" << qPrintable(n) << "\" with " << state->context()->argumentCount << " args" << std::endl; +// for (unsigned i = 0; i < state->context()->variableEnvironment->argumentCount; ++i) +// std::cerr << " " << i << ": " << currentArg(i) << std::endl; +#endif // DO_TRACE_INSTR +} + +void Debugger::leaveFunction(FunctionState *state) +{ + _callStack[callIndex(state->context())].state = 0; +} + +void Debugger::aboutToThrow(const VM::Value &value) +{ + qDebug() << "*** We are about to throw...:" << value.toString(currentState()->context())->toQString(); +} + +FunctionState *Debugger::currentState() const +{ + if (_callStack.isEmpty()) + return 0; + else + return _callStack.last().state; +} + +const char *Debugger::currentArg(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->argument(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentLocal(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->local(idx)->toString(state->context())->toQString()); +} + +const char *Debugger::currentTemp(unsigned idx) const +{ + FunctionState *state = currentState(); + return qPrintable(state->temp(idx)->toString(state->context())->toQString()); +} + +void Debugger::printStackTrace() const +{ + for (int i = _callStack.size() - 1; i >=0; --i) { + QString n = name(_callStack[i].function); + std::cerr << "\tframe #" << i << ": " << qPrintable(n) << std::endl; + } +} + +int Debugger::callIndex(VM::ExecutionContext *context) +{ + for (int idx = _callStack.size() - 1; idx >= 0; --idx) { + if (_callStack[idx].context == context) + return idx; + } + + return -1; +} + +V4IR::Function *Debugger::irFunction(VM::Function *vmf) const +{ + return _vmToIr[vmf]; +} + +static void realDumpValue(VM::Value v, VM::ExecutionContext *ctx, std::string prefix) +{ + using namespace VM; + using namespace std; + cout << prefix << "tag: " << hex << v.tag << dec << endl << prefix << "\t-> "; + switch (v.type()) { + case Value::Undefined_Type: cout << "Undefined" << endl; return; + case Value::Null_Type: cout << "Null" << endl; return; + case Value::Boolean_Type: cout << "Boolean"; break; + case Value::Integer_Type: cout << "Integer"; break; + case Value::Object_Type: cout << "Object"; break; + case Value::String_Type: cout << "String"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + if (v.isBoolean()) { + cout << prefix << "\t-> " << (v.booleanValue() ? "TRUE" : "FALSE") << endl; + return; + } + + if (v.isInteger()) { + cout << prefix << "\t-> " << v.integerValue() << endl; + return; + } + + if (v.isDouble()) { + cout << prefix << "\t-> " << v.doubleValue() << endl; + return; + } + + if (v.isString()) { + // maybe check something on the Managed object? + cout << prefix << "\t-> @" << hex << v.stringValue() << endl; + cout << prefix << "\t-> \"" << qPrintable(v.stringValue()->toQString()) << "\"" << endl; + return; + } + + Object *o = v.objectValue(); + if (!o) + return; + + cout << prefix << "\t-> @" << hex << o << endl; + cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> "; + switch (o->internalType()) { + case VM::Managed::Type_Invalid: cout << "Invalid"; break; + case VM::Managed::Type_String: cout << "String"; break; + case VM::Managed::Type_Object: cout << "Object"; break; + case VM::Managed::Type_ArrayObject: cout << "ArrayObject"; break; + case VM::Managed::Type_FunctionObject: cout << "FunctionObject"; break; + case VM::Managed::Type_BooleanObject: cout << "BooleanObject"; break; + case VM::Managed::Type_NumberObject: cout << "NumberObject"; break; + case VM::Managed::Type_StringObject: cout << "StringObject"; break; + case VM::Managed::Type_DateObject: cout << "DateObject"; break; + case VM::Managed::Type_RegExpObject: cout << "RegExpObject"; break; + case VM::Managed::Type_ErrorObject: cout << "ErrorObject"; break; + case VM::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break; + case VM::Managed::Type_JSONObject: cout << "JSONObject"; break; + case VM::Managed::Type_MathObject: cout << "MathObject"; break; + case VM::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + cout << prefix << "properties:" << endl; + ForEachIteratorObject it(ctx, o); + for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) { + cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl; + PropertyAttributes attrs; + Property *d = o->__getOwnProperty__(name.stringValue(), &attrs); + Value pval = o->getValue(ctx, d, attrs); + cout << prefix << "\tvalue:" << endl; + realDumpValue(pval, ctx, prefix + "\t"); + } +} + +void dumpValue(VM::Value v, VM::ExecutionContext *ctx) +{ + realDumpValue(v, ctx, std::string("")); +} diff --git a/src/qml/qml/v4vm/debugging.h b/src/qml/qml/v4vm/debugging.h new file mode 100644 index 0000000000..b337f41c51 --- /dev/null +++ b/src/qml/qml/v4vm/debugging.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include "qv4global.h" +#include "qv4engine.h" +#include "qv4context.h" + +#include <QHash> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +namespace V4IR { +struct BasicBlock; +struct Function; +} // namespace IR + +namespace Debugging { + +class Debugger; + +struct FunctionDebugInfo { // TODO: use opaque d-pointers here + QString name; + unsigned startLine, startColumn; + + FunctionDebugInfo(V4IR::Function *function): + startLine(0), startColumn(0) + { + if (function->name) + name = *function->name; + } + + void setSourceLocation(unsigned line, unsigned column) + { startLine = line; startColumn = column; } +}; + +class FunctionState +{ +public: + FunctionState(VM::ExecutionContext *context); + virtual ~FunctionState(); + + virtual VM::Value *argument(unsigned idx); + virtual VM::Value *local(unsigned idx); + virtual VM::Value *temp(unsigned idx) = 0; + + VM::ExecutionContext *context() const + { return _context; } + + Debugger *debugger() const + { return _context->engine->debugger; } + +private: + VM::ExecutionContext *_context; +}; + +struct CallInfo +{ + VM::ExecutionContext *context; + VM::FunctionObject *function; + FunctionState *state; + + CallInfo(VM::ExecutionContext *context = 0, VM::FunctionObject *function = 0, FunctionState *state = 0) + : context(context) + , function(function) + , state(state) + {} +}; + +class Q_V4_EXPORT Debugger +{ +public: + Debugger(VM::ExecutionEngine *_engine); + ~Debugger(); + +public: // compile-time interface + void addFunction(V4IR::Function *function); + void setSourceLocation(V4IR::Function *function, unsigned line, unsigned column); + void mapFunction(VM::Function *vmf, V4IR::Function *irf); + +public: // run-time querying interface + FunctionDebugInfo *debugInfo(VM::FunctionObject *function) const; + QString name(VM::FunctionObject *function) const; + +public: // execution hooks + void aboutToCall(VM::FunctionObject *function, VM::ExecutionContext *context); + void justLeft(VM::ExecutionContext *context); + void enterFunction(FunctionState *state); + void leaveFunction(FunctionState *state); + void aboutToThrow(const VM::Value &value); + +public: // debugging hooks + FunctionState *currentState() const; + const char *currentArg(unsigned idx) const; + const char *currentLocal(unsigned idx) const; + const char *currentTemp(unsigned idx) const; + void printStackTrace() const; + +private: + int callIndex(VM::ExecutionContext *context); + V4IR::Function *irFunction(VM::Function *vmf) const; + +private: // TODO: use opaque d-pointers here + VM::ExecutionEngine *_engine; + QHash<V4IR::Function *, FunctionDebugInfo *> _functionInfo; + QHash<VM::Function *, V4IR::Function *> _vmToIr; + QVector<CallInfo> _callStack; +}; + +} // namespace Debugging +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // DEBUGGING_H diff --git a/src/qml/qml/v4vm/llvm_installation.pri b/src/qml/qml/v4vm/llvm_installation.pri new file mode 100644 index 0000000000..99e955fd2b --- /dev/null +++ b/src/qml/qml/v4vm/llvm_installation.pri @@ -0,0 +1,23 @@ +LLVM_CONFIG=llvm-config +# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config +exists ($${LLVM_CONFIG}) { + CONFIG += llvm-libs + message("Found LLVM in $$LLVM_INSTALL_DIR") +} + +llvm-libs { + win32 { + LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include +# TODO: check if the next line is needed somehow for the llvm_runtime target. + LLVM_LIBS += -ladvapi32 -lshell32 + } + + unix { + LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir) + LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir) + } + + LLVM_DEFINES += __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS +} diff --git a/src/qml/qml/v4vm/llvm_runtime.cpp b/src/qml/qml/v4vm/llvm_runtime.cpp new file mode 100644 index 0000000000..629ee4028c --- /dev/null +++ b/src/qml/qml/v4vm/llvm_runtime.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4runtime.h" +#include "qv4context.h" +#include "qv4engine.h" +#include <stdio.h> +#include <setjmp.h> + +using namespace QQmlJS::VM; + +extern "C" { + +Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) +{ + return *result; +} + +Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) +{ + assert(ctx->type == ExecutionContext::Type_CallContext); + return &static_cast<CallContext *>(ctx)->arguments[index]; +} + +void __qmljs_llvm_init_undefined(Value *result) +{ + *result = Value::undefinedValue(); +} + +void __qmljs_llvm_init_null(Value *result) +{ + *result = Value::nullValue(); +} + +void __qmljs_llvm_init_boolean(Value *result, bool value) +{ + *result = Value::fromBoolean(value); +} + +void __qmljs_llvm_init_number(Value *result, double value) +{ + *result = Value::fromDouble(value); +} + +void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) +{ + *result = Value::fromString(ctx->engine->newString(QString::fromUtf8(str))); +} + +void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, + String *name, bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *clos = __qmljs_register_function(ctx, name, hasDirectEval, + usesArgumentsObject, isStrict, + hasNestedFunctions, + formals, formalCount, + locals, localCount); + __qmljs_init_closure(ctx, result, clos); +} + +bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) +{ + return __qmljs_to_boolean(*value); +} + +void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_and(ctx, result, *left, *right); +} + +void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_or(ctx, result, *left, *right); +} + +void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_xor(ctx, result, *left, *right); +} + +void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_add(ctx, result, *left, *right); +} + +void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sub(ctx, result, *left, *right); +} + +void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mul(ctx, result, *left, *right); +} + +void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_div(ctx, result, *left, *right); +} + +void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mod(ctx, result, *left, *right); +} + +void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shl(ctx, result, *left, *right); +} + +void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shr(ctx, result, *left, *right); +} + +void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ushr(ctx, result, *left, *right); +} + +void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_gt(ctx, result, *left, *right); +} + +void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_lt(ctx, result, *left, *right); +} + +void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ge(ctx, result, *left, *right); +} + +void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_le(ctx, result, *left, *right); +} + +void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_eq(ctx, result, *left, *right); +} + +void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ne(ctx, result, *left, *right); +} + +void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_se(ctx, result, *left, *right); +} + +void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sne(ctx, result, *left, *right); +} + +void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_instanceof(ctx, result, *left, *right); +} + +void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_in(ctx, result, *left, *right); +} + +void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_uplus(result, *value); +} + +void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_uminus(result, *value); +} + +void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_compl(result, *value); +} + +void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_not(result, *value); +} + +void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_and_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_or_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_xor_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_add_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_sub_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mul_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_div_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mod_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shl_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shr_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_ushr_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_and_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_or_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_xor_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_add_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_sub_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mul_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_div_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mod_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shl_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shr_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_ushr_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_and_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_or_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_xor_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_add_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_sub_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mul_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_div_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mod_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shl_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shr_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_ushr_member(ctx, *base, member, *value); +} + +String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) +{ + return ctx->engine->newIdentifier(QString::fromUtf8(str)); +} + +void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_call_activation_property(context, result, name, args, argc); +} + +void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +{ + __qmljs_call_value(context, result, thisObject, *func, args, argc); +} + +void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_construct_activation_property(context, result, name, args, argc); +} + +void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) +{ + __qmljs_construct_value(context, result, *func, args, argc); +} + +void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + __qmljs_get_activation_property(ctx, result, name); +} + +void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) +{ + __qmljs_set_activation_property(ctx, name, *value); +} + +void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) +{ + __qmljs_get_property(ctx, result, *object, name); +} + +void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_call_property(context, result, *base, name, args, argc); +} + +void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_construct_property(context, result, *base, name, args, argc); +} + +void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) +{ + __qmljs_get_element(ctx, result, *object, *index); +} + +void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) +{ + __qmljs_set_element(ctx, *object, *index, *value); +} + +void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) +{ + __qmljs_set_property(ctx, *object, name, *value); +} + +void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + __qmljs_builtin_declare_var(ctx, deletable, name); +} + +void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_builtin_typeof(ctx, result, *value); +} + +void __qmljs_llvm_throw(ExecutionContext *context, Value *value) +{ + __qmljs_throw(context, *value); +} + +void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) +{ + // ### FIXME. +} + +void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) +{ + __qmljs_foreach_iterator_object(context, result, *in); +} + +void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) +{ + __qmljs_foreach_next_property_name(result, *it); +} + +void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) +{ + *result = ctx->thisObject; +} + +void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) +{ + __qmljs_delete_subscript(ctx, result, *base, *index); +} + +void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) +{ + __qmljs_delete_member(ctx, result, *base, name); +} + +void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + __qmljs_delete_name(ctx, result, name); +} + +} // extern "C" diff --git a/src/qml/qml/v4vm/moth/moth.pri b/src/qml/qml/v4vm/moth/moth.pri new file mode 100644 index 0000000000..73bd893286 --- /dev/null +++ b/src/qml/qml/v4vm/moth/moth.pri @@ -0,0 +1,13 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4vme_moth_p.h + +SOURCES += \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4vme_moth.cpp + +#DEFINES += DO_TRACE_INSTR diff --git a/src/qml/qml/v4vm/moth/qv4instr_moth.cpp b/src/qml/qml/v4vm/moth/qv4instr_moth.cpp new file mode 100644 index 0000000000..a2bad39e00 --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4instr_moth.cpp @@ -0,0 +1,15 @@ +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/qml/qml/v4vm/moth/qv4instr_moth_p.h b/src/qml/qml/v4vm/moth/qv4instr_moth_p.h new file mode 100644 index 0000000000..b29fb13a74 --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4instr_moth_p.h @@ -0,0 +1,527 @@ +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include <QtCore/qglobal.h> +#include "qv4object.h" + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(EnterTry, enterTry) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinFinishTry, callBuiltinFinishTry) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArray, callBuiltinDefineArray) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3, + ScopedLocalType = 4 + }; + VM::Value value; + unsigned type : 3; + unsigned scope : 29; + unsigned index; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + bool isScopedLocal() const { return type == ScopedLocalType; } + + static Param createValue(const VM::Value &v) + { + Param p; + p.type = ValueType; + p.scope = 0; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx, uint scope) + { + Param p; + p.type = ArgumentType; + p.scope = scope; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createScopedLocal(unsigned idx, uint scope) + { + Param p; + p.type = ScopedLocalType; + p.scope = scope; + p.index = idx; + return p; + } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + VM::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + VM::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_enterTry { + MOTH_INSTR_HEADER + ptrdiff_t tryOffset; + ptrdiff_t catchOffset; + VM::String *exceptionVarName; + Param exceptionVar; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinFinishTry { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + VM::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArray { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param result; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + VM::UnaryOpName alu; + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + VM::BinOp alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + VM::InplaceBinOpElement alu; + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + VM::InplaceBinOpMember alu; + VM::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + VM::InplaceBinOpName alu; + VM::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_enterTry enterTry; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinFinishTry callBuiltinFinishTry; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArray callBuiltinDefineArray; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template<int N> +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template<int InstrType> +class InstrData : public InstrMeta<InstrType>::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/qml/qml/v4vm/moth/qv4isel_moth.cpp b/src/qml/qml/v4vm/moth/qv4isel_moth.cpp new file mode 100644 index 0000000000..00ae4e1029 --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4isel_moth.cpp @@ -0,0 +1,812 @@ +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4functionobject.h" +#include "qv4regexpobject.h" +#include "debugging.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +inline VM::BinOp aluOpFunction(V4IR::AluOp op) +{ + switch (op) { + case V4IR::OpInvalid: + return 0; + case V4IR::OpIfTrue: + return 0; + case V4IR::OpNot: + return 0; + case V4IR::OpUMinus: + return 0; + case V4IR::OpUPlus: + return 0; + case V4IR::OpCompl: + return 0; + case V4IR::OpBitAnd: + return VM::__qmljs_bit_and; + case V4IR::OpBitOr: + return VM::__qmljs_bit_or; + case V4IR::OpBitXor: + return VM::__qmljs_bit_xor; + case V4IR::OpAdd: + return VM::__qmljs_add; + case V4IR::OpSub: + return VM::__qmljs_sub; + case V4IR::OpMul: + return VM::__qmljs_mul; + case V4IR::OpDiv: + return VM::__qmljs_div; + case V4IR::OpMod: + return VM::__qmljs_mod; + case V4IR::OpLShift: + return VM::__qmljs_shl; + case V4IR::OpRShift: + return VM::__qmljs_shr; + case V4IR::OpURShift: + return VM::__qmljs_ushr; + case V4IR::OpGt: + return VM::__qmljs_gt; + case V4IR::OpLt: + return VM::__qmljs_lt; + case V4IR::OpGe: + return VM::__qmljs_ge; + case V4IR::OpLe: + return VM::__qmljs_le; + case V4IR::OpEqual: + return VM::__qmljs_eq; + case V4IR::OpNotEqual: + return VM::__qmljs_ne; + case V4IR::OpStrictEqual: + return VM::__qmljs_se; + case V4IR::OpStrictNotEqual: + return VM::__qmljs_sne; + case V4IR::OpInstanceof: + return VM::__qmljs_instanceof; + case V4IR::OpIn: + return VM::__qmljs_in; + case V4IR::OpAnd: + return 0; + case V4IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + +} // anonymous namespace + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(VM::Function *vmFunction, V4IR::Function *function) +{ + V4IR::BasicBlock *block; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > patches; + QHash<V4IR::BasicBlock *, ptrdiff_t> addrs; + + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + foreach (_block, _function->basicBlocks) { + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (V4IR::Stmt *s, _block->statements) + s->accept(this); + } + + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, + V4IR::ExprList *args, + V4IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + VM::Value v = VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + + Instruction::LoadValue load; + load.value = Instr::Param::createValue(v); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(*name->id); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + addInstruction(move); +} + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + VM::UnaryOpName op = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: op = VM::__qmljs_not; break; + case V4IR::OpUMinus: op = VM::__qmljs_uminus; break; + case V4IR::OpUPlus: op = VM::__qmljs_uplus; break; + case V4IR::OpCompl: op = VM::__qmljs_compl; break; + case V4IR::OpIncrement: op = VM::__qmljs_increment; break; + case V4IR::OpDecrement: op = VM::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target) +{ + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + VM::InplaceBinOpName op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; + case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; + case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; + case V4IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; + case V4IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; + case V4IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; + case V4IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; + case V4IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; + case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; + case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; + case V4IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(rightSource); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + VM::InplaceBinOpElement op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; + case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; + case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; + case V4IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; + case V4IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; + case V4IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; + case V4IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; + case V4IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; + case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; + case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; + case V4IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(source); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + VM::InplaceBinOpMember op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; + case V4IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; + case V4IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; + case V4IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; + case V4IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; + case V4IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; + case V4IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; + case V4IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; + case V4IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; + case V4IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; + case V4IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0 && e->expr->asTemp()) { + // ok, only 1 argument in the call... + const int idx = e->expr->asTemp()->index; + // We can only pass a reference into the stack, which holds temps that + // are not arguments (idx >= 0) nor locals (idx >= localCound). + singleArgIsTemp = idx >= _function->locals.size() && e->expr->asTemp()->scope == 0; + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = e->expr->asTemp()->index - _function->locals.size(); + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + + _patches[s->target].append(loc); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + Instr::Param condition; + if (V4IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (V4IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump jump; + jump.offset = 0; + jump.condition = condition; + ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(trueLoc); + + if (_block->index + 1 != s->iffalse->index) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(falseLoc); + } +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + Instruction::EnterTry enterTry; + enterTry.tryOffset = 0; + enterTry.catchOffset = 0; + enterTry.exceptionVarName = identifier(t->exceptionVarName); + enterTry.exceptionVar = getParam(t->exceptionVar); + ptrdiff_t enterTryLoc = addInstruction(enterTry); + + ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); + _patches[t->tryBlock].append(tryLoc); + + ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); + _patches[t->catchBlock].append(catchLoc); +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + Instruction::CallBuiltinFinishTry call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + Instruction::CallBuiltinPushScope call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopScope() +{ + Instruction::CallBuiltinPopScope call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + Instruction::CallBuiltinDefineArray call; + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; +#else + instr.common.instructionType = type; +#endif + + int instructionSize = Instr::size(type); + if (_codeEnd - _codeNext < instructionSize) { + int currSize = _codeEnd - _codeStart; + uchar *newCode = new uchar[currSize * 2]; + ::memset(newCode + currSize, 0, currSize); + ::memcpy(newCode, _codeStart, currSize); + _codeNext = _codeNext - _codeStart + newCode; + delete[] _codeStart; + _codeStart = newCode; + _codeEnd = _codeStart + currSize * 2; + } + + ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize); + ptrdiff_t ptrOffset = _codeNext - _codeStart; + _codeNext += instructionSize; + + return ptrOffset; +} + +void InstructionSelection::patchJumpAddresses() +{ + typedef QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + Q_ASSERT(_addrs.contains(i.key())); + ptrdiff_t target = _addrs.value(i.key()); + + const QVector<ptrdiff_t> &patchList = i.value(); + for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_codeStart + patch)) = target - patch; + } + } + + _patches.clear(); + _addrs.clear(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +VM::String *InstructionSelection::identifier(const QString &s) +{ + VM::String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} diff --git a/src/qml/qml/v4vm/moth/qv4isel_moth_p.h b/src/qml/qml/v4vm/moth/qv4isel_moth_p.h new file mode 100644 index 0000000000..5e057139e2 --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4isel_moth_p.h @@ -0,0 +1,169 @@ +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include "qv4global.h" +#include "qv4isel_p.h" +#include "qv4jsir_p.h" +#include "qv4object.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace Moth { + +class Q_V4_EXPORT InstructionSelection: + public V4IR::InstructionSelection, + public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(VM::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + +private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + Instr::Param getParam(V4IR::Expr *e) + { + typedef Instr::Param Param; + assert(e); + + if (V4IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (V4IR::Temp *t = e->asTemp()) { + const int index = t->index; + if (index < 0) { + return Param::createArgument(-index - 1, t->scope); + } else if (!t->scope) { + const int localCount = _function->locals.size(); + if (index < localCount) + return Param::createLocal(index); + else + return Param::createTemp(index - localCount); + } else { + return Param::createScopedLocal(t->index, t->scope); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } + } + + Instr::Param getResultParam(V4IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(V4IR::Move *); + void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount - _function->locals.size(); } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template <int Instr> + inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + VM::String *identifier(const QString &s); + + V4IR::Function *_function; + VM::Function *_vmFunction; + V4IR::BasicBlock *_block; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > _patches; + QHash<V4IR::BasicBlock *, ptrdiff_t> _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; +}; + +class Q_V4_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } +}; + +template<int InstrT> +ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) +{ + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/qml/v4vm/moth/qv4vme_moth.cpp b/src/qml/qml/v4vm/moth/qv4vme_moth.cpp new file mode 100644 index 0000000000..a3c34bf340 --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4vme_moth.cpp @@ -0,0 +1,532 @@ +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include "qv4value.h" +#include "debugging.h" + +#include <iostream> + +#include "qv4alloca_p.h" + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +class FunctionState: public Debugging::FunctionState +{ +public: + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) + : Debugging::FunctionState(context) + , stack(0) + , stackSize(0) + , code(code) + {} + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } + +private: + VM::Value *stack; + unsigned stackSize; + const uchar **code; +}; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +#ifdef WITH_STATS +namespace { +struct VMStats { + quint64 paramIsValue; + quint64 paramIsArg; + quint64 paramIsLocal; + quint64 paramIsTemp; + quint64 paramIsScopedLocal; + + VMStats() + : paramIsValue(0) + , paramIsArg(0) + , paramIsLocal(0) + , paramIsTemp(0) + , paramIsScopedLocal(0) + {} + + ~VMStats() + { show(); } + + void show() { + fprintf(stderr, "VM stats:\n"); + fprintf(stderr, " value: %lu\n", paramIsValue); + fprintf(stderr, " arg: %lu\n", paramIsArg); + fprintf(stderr, " local: %lu\n", paramIsLocal); + fprintf(stderr, " temp: %lu\n", paramIsTemp); + fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal); + } +}; +static VMStats vmStats; +#define VMSTATS(what) ++vmStats.what +} +#else // !WITH_STATS +#define VMSTATS(what) {} +#endif // WITH_STATS + +static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, + VM::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) +{ +#ifdef DO_TRACE_INSTR + if (param.isValue()) { + fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData()); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d@%d\n", param.index, param.scope); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); + } else if (param.isScopedLocal()) { + fprintf(stderr, " temp %d@%d\n", param.index, param.scope); + } else { + Q_ASSERT(!"INVALID"); + } +#endif // DO_TRACE_INSTR + + if (param.isValue()) { + VMSTATS(paramIsValue); + return const_cast<VM::Value *>(¶m.value); + } else if (param.isArgument()) { + VMSTATS(paramIsArg); + VM::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + VM::CallContext *cc = static_cast<VM::CallContext *>(c); + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < cc->argumentCount); + Q_ASSERT(cc->arguments); + return cc->arguments + arg; + } else if (param.isLocal()) { + VMSTATS(paramIsLocal); + const unsigned index = param.index; + VM::CallContext *c = static_cast<VM::CallContext *>(context); + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(c->locals); + return c->locals + index; + } else if (param.isTemp()) { + VMSTATS(paramIsTemp); + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else if (param.isScopedLocal()) { + VMSTATS(paramIsScopedLocal); + VM::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + const unsigned index = param.index; + VM::CallContext *cc = static_cast<VM::CallContext *>(c); + Q_ASSERT(index >= 0); + Q_ASSERT(index < cc->variableCount()); + Q_ASSERT(cc->locals); + return cc->locals + index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) *(VALUEPTR(param)) + +// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local. +# define VALUEPTR(param) \ + param.isTemp() ? stack + param.index \ + : (param.isLocal() ? static_cast<VM::CallContext *>(context)->locals + param.index \ + : getValueRef(context, stack, param)) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +VM::Value VME::run(QQmlJS::VM::ExecutionContext *context, const uchar *&code, + VM::Value *stack, unsigned stackSize +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return VM::Value::undefinedValue(); + } +#endif + + FunctionState state(context, &code); + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast<VM::Value *>(alloca(stackSize * sizeof(VM::Value))); + state.setStack(stack, stackSize); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (VM::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_builtin_throw(context, VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(EnterTry) + VALUE(instr.exceptionVar) = VM::Value::undefinedValue(); + try { + const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; + run(context, tryCode, stack, stackSize); + code = tryCode; + } catch (VM::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + try { + VM::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(catchContext, catchCode, stack, stackSize); + code = catchCode; + context = __qmljs_builtin_pop_scope(catchContext); + } catch (VM::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(context, catchCode, stack, stackSize); + code = catchCode; + } + } + MOTH_END_INSTR(EnterTry) + + MOTH_BEGIN_INSTR(CallBuiltinFinishTry) + return VM::Value(); + MOTH_END_INSTR(CallBuiltinFinishTry) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArray) + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); + MOTH_END_INSTR(CallBuiltinDefineArray) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc <= stackSize); + VM::Value *args = stack + instr.args; + __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition)); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.source)); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(Ret) + VM::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = context->thisObject; + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(context, + VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source)); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(context, + VALUE(instr.base), + instr.member, + VALUE(instr.source)); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme.run(ctxt, code); +} diff --git a/src/qml/qml/v4vm/moth/qv4vme_moth_p.h b/src/qml/qml/v4vm/moth/qv4vme_moth_p.h new file mode 100644 index 0000000000..9eea23901a --- /dev/null +++ b/src/qml/qml/v4vm/moth/qv4vme_moth_p.h @@ -0,0 +1,35 @@ +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include "qv4runtime.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace VM { + struct Value; +} + +namespace Moth { + +class VME +{ +public: + static VM::Value exec(VM::ExecutionContext *, const uchar *); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + VM::Value run(QQmlJS::VM::ExecutionContext *, const uchar *&code, + VM::Value *stack = 0, unsigned stackSize = 0 +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4VME_MOTH_P_H diff --git a/src/qml/qml/v4vm/qcalculatehash_p.h b/src/qml/qml/v4vm/qcalculatehash_p.h new file mode 100644 index 0000000000..36c5a6807a --- /dev/null +++ b/src/qml/qml/v4vm/qcalculatehash_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtV8 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CALCULATEHASH_P_H +#define CALCULATEHASH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qstring.h> +#include "qv4v8.h" +QT_BEGIN_NAMESPACE + +inline uint32_t calculateHash(const quint8* chars, int length) +{ + return v8::String::ComputeHash((char *)chars, length); +} + +inline uint32_t calculateHash(const quint16* chars, int length) +{ + return v8::String::ComputeHash((uint16_t *)chars, length); +} + +QT_END_NAMESPACE + +#endif // CALCULATEHASH_P_H diff --git a/src/qml/qml/v4vm/qv4_llvm_p.h b/src/qml/qml/v4vm/qv4_llvm_p.h new file mode 100644 index 0000000000..189fe8586f --- /dev/null +++ b/src/qml/qml/v4vm/qv4_llvm_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4_LLVM_P_H +#define QV4_LLVM_P_H + +#include "qv4global.h" +#include "qv4jsir_p.h" + +#include <QtCore/QString> + +namespace QQmlJS { + +// Note: keep this enum in sync with the command-line option! +enum LLVMOutputType { + LLVMOutputJit = -1, + LLVMOutputIR = 0, // .ll + LLVMOutputBitcode = 1, // .bc + LLVMOutputAssembler = 2, // .s + LLVMOutputObject = 3 // .o +}; + +Q_V4_EXPORT int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*)(void *)); + +} // QQmlJS + +#endif // QV4_LLVM_P_H diff --git a/src/qml/qml/v4vm/qv4alloca_p.h b/src/qml/qml/v4vm/qv4alloca_p.h new file mode 100644 index 0000000000..1e9388c48c --- /dev/null +++ b/src/qml/qml/v4vm/qv4alloca_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4_ALLOCA_H +#define QV4_ALLOCA_H + +#include <qglobal.h> + +#if defined(Q_OS_WIN) +#include <malloc.h> +#define alloca _alloca +#else +#include <alloca.h> +#endif + +#endif diff --git a/src/qml/qml/v4vm/qv4argumentsobject.cpp b/src/qml/qml/v4vm/qv4argumentsobject.cpp new file mode 100644 index 0000000000..21c72be460 --- /dev/null +++ b/src/qml/qml/v4vm/qv4argumentsobject.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4argumentsobject.h> + +namespace QQmlJS { +namespace VM { + + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ArgumentsObject); + +ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount) + : Object(context->engine), context(context) +{ + vtbl = &static_vtbl; + type = Type_ArgumentsObject; + + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::put(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + } else { + uint numAccessors = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->argument(i)); + __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor); + } + for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + Property pd = Property::fromValue(context->argument(i)); + __defineOwnProperty__(context, i, pd, Attr_Data); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + isNonStrictArgumentsObject = true; + } +} + +void ArgumentsObject::destroy(Managed *that) +{ + static_cast<ArgumentsObject *>(that)->~ArgumentsObject(); +} + +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + Property *pd = arrayData + pidx; + Property map; + PropertyAttributes mapAttrs; + bool isMapped = false; + if (pd && index < (uint)mappedArguments.size()) + isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); + + if (isMapped) { + map = *pd; + mapAttrs = arrayAttributes[pidx]; + arrayAttributes[pidx] = Attr_Data; + pd->value = mappedArguments.at(index); + } + + isNonStrictArgumentsObject = false; + bool strict = ctx->strictMode; + ctx->strictMode = false; + bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); + ctx->strictMode = strict; + isNonStrictArgumentsObject = true; + + if (isMapped && attrs.isData()) { + if (!attrs.isGeneric()) { + Value arg = desc.value; + map.setter()->call(ctx, Value::fromObject(this), &arg, 1); + } + if (attrs.isWritable()) { + *pd = map; + arrayAttributes[pidx] = mapAttrs; + } + } + + if (ctx->strictMode && !result) + ctx->throwTypeError(); + return result; +} + +DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); + +Value ArgumentsGetterFunction::call(Managed *getter, ExecutionContext *ctx, const Value &thisObject, Value *, int) +{ + ArgumentsGetterFunction *g = static_cast<ArgumentsGetterFunction *>(getter); + Object *that = thisObject.asObject(); + if (!that) + ctx->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + ctx->throwTypeError(); + + assert(g->index < o->context->argumentCount); + return o->context->argument(g->index); +} + +DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction); + +Value ArgumentsSetterFunction::call(Managed *setter, ExecutionContext *ctx, const Value &thisObject, Value *args, int argc) +{ + ArgumentsSetterFunction *s = static_cast<ArgumentsSetterFunction *>(setter); + Object *that = thisObject.asObject(); + if (!that) + ctx->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + ctx->throwTypeError(); + + assert(s->index < o->context->argumentCount); + o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + +void ArgumentsObject::markObjects(Managed *that) +{ + ArgumentsObject *o = static_cast<ArgumentsObject *>(that); + o->context->mark(); + for (int i = 0; i < o->mappedArguments.size(); ++i) { + Managed *m = o->mappedArguments.at(i).asManaged(); + if (m) + m->mark(); + } + Object::markObjects(that); +} + +} +} diff --git a/src/qml/qml/v4vm/qv4argumentsobject.h b/src/qml/qml/v4vm/qv4argumentsobject.h new file mode 100644 index 0000000000..6727d8759b --- /dev/null +++ b/src/qml/qml/v4vm/qv4argumentsobject.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include <qv4object.h> +#include <qv4functionobject.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ArgumentsObject: Object { + CallContext *context; + QVector<Value> mappedArguments; + ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount); + ~ArgumentsObject() {} + + bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); + + static void markObjects(Managed *that); +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *); +}; + +} +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml/v4vm/qv4arrayobject.cpp b/src/qml/qml/v4vm/qv4arrayobject.cpp new file mode 100644 index 0000000000..90746d008c --- /dev/null +++ b/src/qml/qml/v4vm/qv4arrayobject.cpp @@ -0,0 +1,859 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4arrayobject.h" +#include "qv4sparsearray.h" + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(ArrayCtor); + +ArrayCtor::ArrayCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value ArrayCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) +{ + ArrayObject *a = ctx->engine->newArrayObject(ctx); + uint len; + if (argc == 1 && argv[0].isNumber()) { + bool ok; + len = argv[0].asArrayLength(&ok); + + if (!ok) + ctx->throwRangeError(argv[0]); + + if (len < 0x1000) + a->arrayReserve(len); + } else { + len = argc; + a->arrayReserve(len); + for (unsigned int i = 0; i < len; ++i) + a->arrayData[i].value = argv[i]; + a->arrayDataLen = len; + } + a->setArrayLengthUnchecked(len); + + return Value::fromObject(a); +} + +Value ArrayCtor::call(Managed *that, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc) +{ + return construct(that, ctx, argv, argc); +} + +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); +} + +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArrayObject()) + return o->arrayLength(); + return o->get(ctx, ctx->engine->id_length).toUInt32(); +} + +Value ArrayPrototype::method_isArray(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + +Value ArrayPrototype::method_toString(SimpleCallContext *ctx) +{ + return method_join(ctx); +} + +Value ArrayPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + return method_toString(ctx); +} + +Value ArrayPrototype::method_concat(SimpleCallContext *ctx) +{ + ArrayObject *result = ctx->engine->newArrayObject(ctx); + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) { + result->copyArrayData(instance); + } else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result->arraySet(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result->arrayConcat(elt); + + else + result->arraySet(getLength(ctx, result), arg); + } + + return Value::fromObject(result); +} + +Value ArrayPrototype::method_join(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber()); + + static QSet<Object *> visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue()); + + QString R; + + // ### FIXME + if (ArrayObject *a = self.asArrayObject()) { + for (uint i = 0; i < a->arrayLength(); ++i) { + if (i) + R += r4; + + Value e = a->getIndexed(ctx, i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromDouble(k).toString(ctx); + Value r12 = self.property(ctx, name); + + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue()); + return Value::fromString(ctx, R); +} + +Value ArrayPrototype::method_pop(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->getIndexed(ctx, len - 1); + + instance->deleteIndexedProperty(ctx, len - 1); + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_push(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (len + ctx->argumentCount < len) { + // ughh... + double l = len; + for (double i = 0; i < ctx->argumentCount; ++i) { + Value idx = Value::fromDouble(l + i); + instance->put(ctx, idx.toString(ctx), ctx->argument(i)); + } + double newLen = l + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + else + ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); + return Value::fromDouble(newLen); + } + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (len >= instance->arrayAlloc) + instance->arrayReserve(len + 1); + instance->arrayData[len].value = v; + if (instance->arrayAttributes) + instance->arrayAttributes[len] = Attr_Data; + instance->arrayDataLen = len + 1; + } else { + uint i = instance->allocArrayValue(v); + instance->sparseArray->push_back(i, len); + } + ++len; + } + } else { + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(ctx, len + i, ctx->argument(i)); + len += ctx->argumentCount; + } + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len); + else + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(len)); + + if (len < INT_MAX) + return Value::fromInt32(len); + return Value::fromDouble((double)len); + +} + +Value ArrayPrototype::method_reverse(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint length = getLength(ctx, instance); + + int lo = 0, hi = length - 1; + + for (; lo < hi; ++lo, --hi) { + bool loExists, hiExists; + Value lval = instance->getIndexed(ctx, lo, &loExists); + Value hval = instance->getIndexed(ctx, hi, &hiExists); + if (hiExists) + instance->putIndexed(ctx, lo, hval); + else + instance->deleteIndexedProperty(ctx, lo); + if (loExists) + instance->putIndexed(ctx, hi, lval); + else + instance->deleteIndexedProperty(ctx, hi); + } + return Value::fromObject(instance); +} + +Value ArrayPrototype::method_shift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx, ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Property *front = 0; + uint pidx = instance->propertyIndexFromArrayIndex(0); + if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric())) + front = instance->arrayData + pidx; + + Value result = front ? instance->getValue(ctx, front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue(); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + if (!instance->sparseArray) { + if (instance->arrayDataLen) { + ++instance->arrayOffset; + ++instance->arrayData; + --instance->arrayDataLen; + --instance->arrayAlloc; + if (instance->arrayAttributes) + ++instance->arrayAttributes; + } + } else { + uint idx = instance->sparseArray->pop_front(); + instance->freeArrayValue(idx); + } + } else { + // do it the slow way + for (uint k = 1; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (exists) + instance->putIndexed(ctx, k - 1, v); + else + instance->deleteIndexedProperty(ctx, k - 1); + } + instance->deleteIndexedProperty(ctx, len - 1); + } + + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_slice(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + + ArrayObject *result = ctx->engine->newArrayObject(ctx); + uint len = o->get(ctx, ctx->engine->id_length).toUInt32(); + double s = ctx->argument(0).toInteger(); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->getIndexed(ctx, i, &exists); + if (exists) { + result->arraySet(n, v); + } + ++n; + } + return Value::fromObject(result); +} + +Value ArrayPrototype::method_sort(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + Value comparefn = ctx->argument(0); + instance->arraySort(ctx, instance, comparefn, len); + return ctx->thisObject; +} + +Value ArrayPrototype::method_splice(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + ArrayObject *newArray = ctx->engine->newArrayObject(ctx); + + double rs = ctx->argument(0).toInteger(); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(), 0.), (double)(len - start)); + + newArray->arrayReserve(deleteCount); + Property *pd = newArray->arrayData; + for (uint i = 0; i < deleteCount; ++i) { + pd->value = instance->getIndexed(ctx, start + i); + ++pd; + } + newArray->arrayDataLen = deleteCount; + newArray->setArrayLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k + deleteCount, &exists); + if (exists) + instance->arraySet(k + itemCount, v); + else + instance->deleteIndexedProperty(ctx, k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->deleteIndexedProperty(ctx, k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->getIndexed(ctx, k + deleteCount - 1, &exists); + if (exists) + instance->arraySet(k + itemCount - 1, v); + else + instance->deleteIndexedProperty(ctx, k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->arraySet(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); +} + +Value ArrayPrototype::method_unshift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (!instance->arrayOffset) + instance->getArrayHeadRoom(); + + --instance->arrayOffset; + --instance->arrayData; + ++instance->arrayDataLen; + if (instance->arrayAttributes) { + --instance->arrayAttributes; + *instance->arrayAttributes = Attr_Data; + } + instance->arrayData->value = v; + } else { + uint idx = instance->allocArrayValue(v); + instance->sparseArray->push_front(idx); + } + } + } else { + for (uint k = len; k > 0; --k) { + bool exists; + Value v = instance->getIndexed(ctx, k - 1, &exists); + if (exists) + instance->putIndexed(ctx, k + ctx->argumentCount - 1, v); + else + instance->deleteIndexedProperty(ctx, k + ctx->argumentCount - 1); + } + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(ctx, i, ctx->argument(i)); + } + + uint newLen = len + ctx->argumentCount; + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(newLen); + else + instance->put(ctx, ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); +} + +Value ArrayPrototype::method_indexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = 0; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isStringObject()) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + + return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance); +} + +Value ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Value::fromInt32(-1); + } + fromIndex = (uint) f + 1; + } + + for (uint k = fromIndex; k > 0;) { + --k; + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); +} + +Value ArrayPrototype::method_every(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + ok = r.toBoolean(); + } + return Value::fromBoolean(ok); +} + +Value ArrayPrototype::method_some(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(ctx, thisArg, args, 3); + if (r.toBoolean()) + return Value::fromBoolean(true); + } + return Value::fromBoolean(false); +} + +Value ArrayPrototype::method_forEach(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + callback->call(ctx, thisArg, args, 3); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_map(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->arrayReserve(len); + a->setArrayLengthUnchecked(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value mapped = callback->call(ctx, thisArg, args, 3); + a->arraySet(k, mapped); + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_filter(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + a->arrayReserve(len); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(ctx, k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value selected = callback->call(ctx, thisArg, args, 3); + if (selected.toBoolean()) { + a->arraySet(to, v); + ++to; + } + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_reduce(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->getIndexed(ctx, k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k < len) { + bool kPresent; + Value v = instance->getIndexed(ctx, k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + ++k; + } + return acc; +} + +Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + if (len == 0) { + if (ctx->argumentCount == 1) + ctx->throwTypeError(); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->getIndexed(ctx, k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k > 0) { + bool kPresent; + Value v = instance->getIndexed(ctx, k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(ctx, Value::undefinedValue(), args, 4); + } + --k; + } + return acc; +} + diff --git a/src/qml/qml/v4vm/qv4arrayobject.h b/src/qml/qml/v4vm/qv4arrayobject.h new file mode 100644 index 0000000000..86d14eb587 --- /dev/null +++ b/src/qml/qml/v4vm/qv4arrayobject.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYOBJECT_H +#define QV4ARRAYOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + + +struct ArrayCtor: FunctionObject +{ + ArrayCtor(ExecutionContext *scope); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArrayPrototype: ArrayObject +{ + ArrayPrototype(ExecutionContext *context) : ArrayObject(context) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static uint getLength(ExecutionContext *ctx, Object *o); + + static Value method_isArray(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_concat(SimpleCallContext *ctx); + static Value method_join(SimpleCallContext *ctx); + static Value method_pop(SimpleCallContext *ctx); + static Value method_push(SimpleCallContext *ctx); + static Value method_reverse(SimpleCallContext *ctx); + static Value method_shift(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_sort(SimpleCallContext *ctx); + static Value method_splice(SimpleCallContext *ctx); + static Value method_unshift(SimpleCallContext *ctx); + static Value method_indexOf(SimpleCallContext *ctx); + static Value method_lastIndexOf(SimpleCallContext *ctx); + static Value method_every(SimpleCallContext *ctx); + static Value method_some(SimpleCallContext *ctx); + static Value method_forEach(SimpleCallContext *ctx); + static Value method_map(SimpleCallContext *ctx); + static Value method_filter(SimpleCallContext *ctx); + static Value method_reduce(SimpleCallContext *ctx); + static Value method_reduceRight(SimpleCallContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4booleanobject.cpp b/src/qml/qml/v4vm/qv4booleanobject.cpp new file mode 100644 index 0000000000..e449e5c7db --- /dev/null +++ b/src/qml/qml/v4vm/qv4booleanobject.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4booleanobject.h" + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(BooleanCtor); + +BooleanCtor::BooleanCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value BooleanCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + bool n = argc ? args[0].toBoolean() : false; + return Value::fromObject(ctx->engine->newBooleanObject(Value::fromBoolean(n))); +} + +Value BooleanCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) +{ + bool value = argc ? argv[0].toBoolean() : 0; + return Value::fromBoolean(value); +} + +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); +} + +Value BooleanPrototype::method_toString(SimpleCallContext *ctx) +{ + bool result; + if (ctx->thisObject.isBoolean()) { + result = ctx->thisObject.booleanValue(); + } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + result = thisObject->value.booleanValue(); + } + + return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); +} + +Value BooleanPrototype::method_valueOf(SimpleCallContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} diff --git a/src/qml/qml/v4vm/qv4booleanobject.h b/src/qml/qml/v4vm/qv4booleanobject.h new file mode 100644 index 0000000000..05b542590a --- /dev/null +++ b/src/qml/qml/v4vm/qv4booleanobject.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4BOOLEANOBJECT_H +#define QBOOLEANOBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct BooleanCtor: FunctionObject +{ + BooleanCtor(ExecutionContext *scope); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BooleanPrototype: BooleanObject +{ + BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4codegen.cpp b/src/qml/qml/v4vm/qv4codegen.cpp new file mode 100644 index 0000000000..c91458282f --- /dev/null +++ b/src/qml/qml/v4vm/qv4codegen.cpp @@ -0,0 +1,3256 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4codegen_p.h" +#include "qv4util.h" +#include "debugging.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QBitArray> +#include <QtCore/QStack> +#include <private/qqmljsast_p.h> +#include <qv4runtime.h> +#include <qv4context.h> +#include <cmath> +#include <iostream> +#include <cassert> + +#ifdef CONST +#undef CONST +#endif + +using namespace QQmlJS; +using namespace AST; + +namespace { +QTextStream qout(stdout, QIODevice::WriteOnly); + +void dfs(V4IR::BasicBlock *block, + QSet<V4IR::BasicBlock *> *V, + QVector<V4IR::BasicBlock *> *blocks) +{ + if (! V->contains(block)) { + V->insert(block); + + foreach (V4IR::BasicBlock *succ, block->out) + dfs(succ, V, blocks); + + blocks->append(block); + } +} + +struct ComputeUseDef: V4IR::StmtVisitor, V4IR::ExprVisitor +{ + V4IR::Function *_function; + V4IR::Stmt *_stmt; + + ComputeUseDef(V4IR::Function *function) + : _function(function) + , _stmt(0) {} + + void operator()(V4IR::Stmt *s) { + assert(! s->d); + s->d = new V4IR::Stmt::Data; + qSwap(_stmt, s); + _stmt->accept(this); + qSwap(_stmt, s); + } + + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(V4IR::Enter *) {} + virtual void visitLeave(V4IR::Leave *) {} + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *t) { + if (! _stmt->d->defs.contains(t->exceptionVar->index)) + _stmt->d->defs.append(t->exceptionVar->index); + } + + virtual void visitTemp(V4IR::Temp *e) { + if (e->index < 0 || e->scope != 0) + return; + + if (! _stmt->d->uses.contains(e->index)) + _stmt->d->uses.append(e->index); + } + + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(V4IR::Move *s) { + if (V4IR::Temp *t = s->target->asTemp()) { + if (t->index >= 0 && t->scope == 0) // only collect unscoped locals and temps + if (! _stmt->d->defs.contains(t->index)) + _stmt->d->defs.append(t->index); + } else { + // source was not a temp, but maybe a sub-expression has a temp + // (e.g. base expressions for subscripts/member-access), + // so visit it. + s->target->accept(this); + } + // whatever the target expr was, always visit the source expr to collect + // temps there. + s->source->accept(this); + } +}; + +void liveness(V4IR::Function *function) +{ + QSet<V4IR::BasicBlock *> V; + QVector<V4IR::BasicBlock *> blocks; + + ComputeUseDef computeUseDef(function); + foreach (V4IR::BasicBlock *block, function->basicBlocks) { + foreach (V4IR::Stmt *s, block->statements) + computeUseDef(s); + } + + dfs(function->basicBlocks.at(0), &V, &blocks); + + bool changed; + do { + changed = false; + + foreach (V4IR::BasicBlock *block, blocks) { + const QBitArray previousLiveIn = block->liveIn; + const QBitArray previousLiveOut = block->liveOut; + QBitArray live(function->tempCount); + foreach (V4IR::BasicBlock *succ, block->out) + live |= succ->liveIn; + block->liveOut = live; + for (int i = block->statements.size() - 1; i != -1; --i) { + V4IR::Stmt *s = block->statements.at(i); + s->d->liveOut = live; + foreach (unsigned d, s->d->defs) + live.clearBit(d); + foreach (unsigned u, s->d->uses) + live.setBit(u); + s->d->liveIn = live; + } + block->liveIn = live; + if (! changed) { + if (previousLiveIn != block->liveIn || previousLiveOut != block->liveOut) + changed = true; + } + } + } while (changed); +} + +static inline bool isDeadAssignment(V4IR::Stmt *stmt, int localCount) +{ + V4IR::Move *move = stmt->asMove(); + if (!move || move->op != V4IR::OpInvalid) + return false; + V4IR::Temp *target = move->target->asTemp(); + if (!target) + return false; + if (target->scope || target->index < localCount) + return false; + + if (V4IR::Name *n = move->source->asName()) { + if (*n->id != QStringLiteral("this")) + return false; + } else if (!move->source->asConst() && !move->source->asTemp()) { + return false; + } + + return !stmt->d->liveOut.at(target->index); +} + +void removeDeadAssignments(V4IR::Function *function) +{ + const int localCount = function->locals.size(); + foreach (V4IR::BasicBlock *bb, function->basicBlocks) { + QVector<V4IR::Stmt *> &statements = bb->statements; + for (int i = 0; i < statements.size(); ) { +// qout<<"removeDeadAssignments: considering ";statements.at(i)->dump(qout);qout<<"\n";qout.flush(); + if (isDeadAssignment(statements.at(i), localCount)) + statements.remove(i); + else + ++i; + } + } +} + +class ConstantPropagation: public V4IR::StmtVisitor, public V4IR::ExprVisitor +{ + struct Value { + enum Type { + InvalidType = 0, + UndefinedType, + NullType, + BoolType, + NumberType, + ThisType, + StringType + } type; + + union { + double numberValue; + V4IR::String *stringValue; + }; + + Value() + : type(InvalidType), stringValue(0) + {} + + explicit Value(V4IR::String *str) + : type(StringType), stringValue(str) + {} + + explicit Value(Type t) + : type(t), stringValue(0) + {} + + Value(Type t, double val) + : type(t), numberValue(val) + {} + + bool isValid() const + { return type != InvalidType; } + + bool operator<(const Value &other) const + { + if (type < other.type) + return true; + if (type == Value::NumberType && other.type == Value::NumberType) { + if (numberValue == 0 && other.numberValue == 0) + return isNegative(numberValue) && !isNegative(other.numberValue); + else + return numberValue < other.numberValue; + } + if (type == Value::BoolType && other.type == Value::BoolType) + return numberValue < other.numberValue; + if (type == Value::StringType && other.type == Value::StringType) + return *stringValue->value < *other.stringValue->value; + return false; + } + + bool operator==(const Value &other) const + { + if (type != other.type) + return false; + if (type == Value::NumberType && other.type == Value::NumberType) { + if (numberValue == 0 && other.numberValue == 0) + return isNegative(numberValue) == isNegative(other.numberValue); + else + return numberValue == other.numberValue; + } + if (type == Value::BoolType && other.type == Value::BoolType) + return numberValue == other.numberValue; + if (type == Value::StringType && other.type == Value::StringType) + return *stringValue->value == *other.stringValue->value; + return false; + } + }; + +public: + void run(V4IR::Function *function) + { + if (function->hasTry) + return; + localCount = function->locals.size(); + if (function->hasWith) { + thisTemp = -1; + } else { + V4IR::BasicBlock *entryBlock = function->basicBlocks.at(0); + thisTemp = entryBlock->newTemp(); + V4IR::Move *fetchThis = function->New<V4IR::Move>(); + fetchThis->init(entryBlock->TEMP(thisTemp), + entryBlock->NAME(QStringLiteral("this"), 0, 0), + V4IR::OpInvalid); + entryBlock->statements.prepend(fetchThis); + } + + foreach (V4IR::BasicBlock *block, function->basicBlocks) { +// qDebug()<<"--- Starting with BB"<<block->index; + reset(); + QVector<V4IR::Stmt *> &statements = block->statements; + foreach (V4IR::Stmt *stmt, statements) { +// qout<<"*** ";stmt->dump(qout);qout<<"\n";qout.flush(); + stmt->accept(this); + } + } + } + +protected: + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(V4IR::Enter *) {} + virtual void visitLeave(V4IR::Leave *) {} + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *) {} + + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitTemp(V4IR::Temp *e) { + if (e->scope) + return; + + const int replacement = tempReplacement.value(e->index, -1); + if (replacement != -1) { +// qDebug() << "+++ Replacing" << e->index << "with" << replacement; + e->index = replacement; + } + } + + virtual void visitMove(V4IR::Move *s) { + V4IR::Temp *targetTemp = s->target->asTemp(); + if (targetTemp && targetTemp->index >= localCount && !targetTemp->scope) { + if (s->op == V4IR::OpInvalid) { + if (V4IR::Name *n = s->source->asName()) { + if (thisTemp != -1) { + if (*n->id == QStringLiteral("this")) { + check(targetTemp->index, Value(Value::ThisType)); + return; + } + } + } else if (V4IR::Const *c = s->source->asConst()) { + Value value; + switch (c->type) { + case V4IR::UndefinedType: value.type = Value::UndefinedType; break; + case V4IR::NullType: value.type = Value::NullType; break; + case V4IR::BoolType: value.type = Value::BoolType; value.numberValue = c->value == 0 ? 0 : 1; break; + case V4IR::NumberType: value.type = Value::NumberType; value.numberValue = c->value; break; + default: Q_ASSERT("unknown const type"); return; + } + check(targetTemp->index, value); + return; + } else if (V4IR::String *str = s->source->asString()) { + check(targetTemp->index, Value(str)); + return; + } + } + invalidate(targetTemp->index, Value()); + } else { + s->target->accept(this); + } + + s->source->accept(this); + } + + void invalidate(int &targetTempIndex, const Value &value) + { + QMap<int, Value>::iterator it = valueForTemp.find(targetTempIndex); + if (it != valueForTemp.end()) { + if (it.value() == value) + return; + tempForValue.remove(it.value()); + valueForTemp.erase(it); + } + + QMap<int, int>::iterator it2 = tempReplacement.find(targetTempIndex); + if (it2 != tempReplacement.end()) { + tempReplacement.erase(it2); + } + } + + void check(int &targetTempIndex, const Value &value) + { + Q_ASSERT(value.isValid()); + + invalidate(targetTempIndex, value); + + int replacementTemp = tempForValue.value(value, -1); + if (replacementTemp == -1) { +// qDebug() << "+++ inserting temp" << targetTempIndex; + tempForValue.insert(value, targetTempIndex); + valueForTemp.insert(targetTempIndex, value); + } else { +// qDebug() << "+++ temp" << targetTempIndex << "can be replaced with" << replacementTemp; + tempReplacement.insert(targetTempIndex, replacementTemp); + } + } + + void reset() + { + tempForValue.clear(); + tempReplacement.clear(); + if (thisTemp != -1) + tempForValue.insert(Value(Value::ThisType), thisTemp); + } + +private: + QMap<Value, int> tempForValue; + QMap<int, Value> valueForTemp; + QMap<int, int> tempReplacement; + + int localCount; + int thisTemp; +}; + +#undef DEBUG_TEMP_COMPRESSION +#ifdef DEBUG_TEMP_COMPRESSION +# define DBTC(x) x +#else // !DEBUG_TEMP_COMPRESSION +# define DBTC(x) +#endif // DEBUG_TEMP_COMPRESSION +class CompressTemps: public V4IR::StmtVisitor, V4IR::ExprVisitor +{ +public: + void run(V4IR::Function *function) + { + _nextFree = 0; + _active.reserve(function->tempCount); + _localCount = function->locals.size(); + + DBTC(qDebug() << "starting on function" << (*function->name) << "with" << (function->tempCount - _localCount) << "temps.";) + + QVector<int> pinned; + foreach (V4IR::BasicBlock *block, function->basicBlocks) { + if (V4IR::Stmt *last = block->terminator()) { + const QBitArray &liveOut = last->d->liveOut; + for (int i = _localCount, ei = liveOut.size(); i < ei; ++i) { + if (liveOut.at(i) && !pinned.contains(i)) { + pinned.append(i); + add(i - _localCount, _nextFree); + } + } + } + } + _pinnedCount = _nextFree; + + int maxUsed = _nextFree; + + foreach (V4IR::BasicBlock *block, function->basicBlocks) { + DBTC(qDebug("L%d:", block->index)); + + for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { + _currentStatement = block->statements[i]; + if (i == 0) + expireOld(); + + DBTC(_currentStatement->dump(qout);qout<<endl<<flush;) + + if (_currentStatement->d) + _currentStatement->accept(this); + } + maxUsed = std::max(maxUsed, _nextFree); + } + DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";) + function->tempCount = maxUsed + _localCount; + } + +protected: + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(V4IR::Enter *) {} + virtual void visitLeave(V4IR::Leave *) {} + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *t) { visitTemp(t->exceptionVar); } + + virtual void visitTemp(V4IR::Temp *e) { + if (e->scope) // scoped local + return; + if (e->index < _localCount) // local or argument + return; + + e->index = remap(e->index - _localCount) + _localCount; + } + + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(V4IR::Move *s) { + s->target->accept(this); + s->source->accept(this); + } + +private: + int remap(int tempIndex) { + for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { + if (i->first == tempIndex) { + DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);) + return i->second; + } + } + + int firstFree = expireOld(); + add(tempIndex, firstFree); + return firstFree; + } + + void add(int tempIndex, int firstFree) { + if (_nextFree <= firstFree) + _nextFree = firstFree + 1; + _active.prepend(qMakePair(tempIndex, firstFree)); + DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);) + } + + int expireOld() { + Q_ASSERT(_currentStatement->d); + + const QBitArray &liveIn = _currentStatement->d->liveIn; + QBitArray inUse(_nextFree); + int i = 0; + while (i < _active.size()) { + const QPair<int, int> &p = _active[i]; + + if (p.second < _pinnedCount) { + inUse.setBit(p.second); + ++i; + continue; + } + + if (liveIn[p.first + _localCount]) { + inUse[p.second] = true; + ++i; + } else { + DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);) + _active.remove(i); + } + } + for (int i = 0, ei = inUse.size(); i < ei; ++i) + if (!inUse[i]) + return i; + return _nextFree; + } + +private: + typedef QVector<QPair<int, int> > ActiveTemps; + ActiveTemps _active; + V4IR::Stmt *_currentStatement; + int _localCount; + int _nextFree; + int _pinnedCount; +}; +#undef DBTC + +} // end of anonymous namespace + +class Codegen::ScanFunctions: Visitor +{ + typedef TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode) + : _cg(cg) + , _sourceCode(sourceCode) + , _env(0) + , _inFuncBody(false) + , _allowFuncDecls(true) + { + } + + void operator()(Node *node) + { + if (node) + node->accept(this); + } + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(SourceElements *ast) + { + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QStringLiteral("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); + } + } + } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + checkDirectivePrologue(ast->elements); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + + virtual bool visit(CallExpression *ast) + { + if (! _env->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _env->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(ArrayLiteral *ast) + { + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + return true; + } + + virtual bool visit(VariableDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + return true; + } + + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + return true; + } + + virtual bool visit(ExpressionStatement *ast) + { + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { + _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token")); + } + } + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + enterFunction(ast, /*enterName*/ false); + return true; + } + + void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); + } + + virtual bool visit(ObjectLiteral *ast) + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; + } + + virtual bool visit(PropertyGetterSetter *ast) + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionDeclaration *ast) + { + enterFunction(ast, /*enterName*/ true, /*isExpression */false); + return true; + } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionBody *ast) + { + TemporaryBoolAssignment inFuncBody(_inFuncBody, true); + Node::accept(ast->elements, this); + return false; + } + + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); + return false; + } + + return true; + } + + virtual bool visit(IfStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->ok, this); + Node::accept(ast->ko, this); + + return false; + } + + virtual bool visit(WhileStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; + } + + virtual bool visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; + } + +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + _env->enter(name, Environment::FunctionDefinition, expr); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _env->formals = formals; + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } + } + +private: // fields: + Codegen *_cg; + const QString _sourceCode; + Environment *_env; + QStack<Environment *> _envStack; + + bool _inFuncBody; + bool _allowFuncDecls; +}; + +Codegen::Codegen(VM::ExecutionContext *context, bool strict) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(context) + , _strictMode(strict) + , _debugger(context->engine->debugger) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(0) + , _strictMode(strictMode) + , _debugger(0) + , _errorHandler(errorHandler) +{ +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + Program *node, + V4IR::Module *module, + Mode mode, + const QStringList &inheritedLocals) +{ + assert(node); + + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + scan(node); + + V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); + if (_debugger) { + if (node->elements->element) { + SourceLocation loc = node->elements->element->firstSourceLocation(); + _debugger->setSourceLocation(globalCode, loc.startLine, loc.startColumn); + } + } + + foreach (V4IR::Function *function, _module->functions) { + linearize(function); + } + + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module) +{ + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + // fake a global environment + scan.enterEnvironment(0); + scan(ast); + scan.leaveEnvironment(); + + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); + + foreach (V4IR::Function *function, _module->functions) { + linearize(function); + } + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + assert(_env); +} + +void Codegen::leaveEnvironment() +{ + assert(_env); + _env = _env->parent; +} + +void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock) +{ + _loop = new Loop(node, breakBlock, continueBlock, _loop); + _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _loop->scopeAndFinally = _scopeAndFinally; + _labelledStatement = 0; +} + +void Codegen::leaveLoop() +{ + Loop *current = _loop; + _loop = _loop->parent; + delete current; +} + +V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name) +{ + if (base->asTemp() /*|| base->asName()*/) + return _block->MEMBER(base->asTemp(), name); + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index) +{ + if (! base->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); +} + +V4IR::Expr *Codegen::argument(V4IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +// keeps references alive, converts other expressions to temps +V4IR::Expr *Codegen::reference(V4IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr) +{ + if (V4IR::Const *c = expr->asConst()) { + if (c->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpNot: + return _block->CONST(V4IR::BoolType, !c->value); + case V4IR::OpUMinus: + return _block->CONST(V4IR::NumberType, -c->value); + case V4IR::OpUPlus: + return expr; + case V4IR::OpCompl: + return _block->CONST(V4IR::NumberType, ~VM::Value::toInt32(c->value)); + case V4IR::OpIncrement: + return _block->CONST(V4IR::NumberType, c->value + 1); + case V4IR::OpDecrement: + return _block->CONST(V4IR::NumberType, c->value - 1); + default: + break; + } + } + } + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); +} + +V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right) +{ + if (V4IR::Const *c1 = left->asConst()) { + if (V4IR::Const *c2 = right->asConst()) { + if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value); + case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0); + case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value)); + case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value)); + case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value)); + case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value); + case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value); + case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value); + case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value); + case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value); + case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, VM::Value::toInt32(c1->value) << (VM::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpMod: return _block->CONST(V4IR::NumberType, ::fmod(c1->value, c2->value)); + case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value); + case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value); + case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, VM::Value::toInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value); + case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,VM::Value::toUInt32(c1->value) >> (VM::Value::toUInt32(c2->value) & 0x1f)); + + case V4IR::OpInstanceof: + case V4IR::OpIn: + break; + + case V4IR::OpIfTrue: // unary ops + case V4IR::OpNot: + case V4IR::OpUMinus: + case V4IR::OpUPlus: + case V4IR::OpCompl: + case V4IR::OpIncrement: + case V4IR::OpDecrement: + case V4IR::OpInvalid: + break; + } + } + } + } else if (op == V4IR::OpAdd) { + if (V4IR::String *s1 = left->asString()) { + if (V4IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (!right->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + assert(left->asTemp()); + assert(right->asTemp()); + + return _block->BINOP(op, left, right); +} + +V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args) +{ + base = reference(base); + return _block->CALL(base, args); +} + +void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op) +{ + assert(target->isLValue()); + + if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + + _block->MOVE(target, source, op); +} + +void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (! (cond->asTemp() || cond->asBinop())) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (! ast) { + return; + } else { + Result r(nx); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + if (r->asCall()) { + _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) + } else if (r->asTemp()) { + // there is nothing to do + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *r); + } + } + } +} + +void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + cjump(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) +{ + UiMember m; + if (ast) { + qSwap(_uiMember, m); + accept(ast); + qSwap(_uiMember, m); + } + return m; +} + +void Codegen::functionBody(FunctionBody *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) { + sourceElements(ast->elements); + } +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + V4IR::Expr *initializer = 0; + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; + + if (! _env->parent || _function->insideWithOrCatch) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { + const int index = _env->findMember(ast->name.toString()); + assert(index != -1); + move(_block->TEMP(index), initializer); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + V4IR::ExprList *args = 0; + V4IR::ExprList *current = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + Result expr = expression(it->expression); + + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + + V4IR::Expr *exp = *expr; + if (exp->asTemp() || exp->asConst()) { + current->expr = exp; + } else { + unsigned value = _block->newTemp(); + move(_block->TEMP(value), exp); + current->expr = _block->TEMP(value); + } + } + for (Elision *elision = ast->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static V4IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return V4IR::OpBitAnd; + case QSOperator::InplaceSub: return V4IR::OpSub; + case QSOperator::InplaceDiv: return V4IR::OpDiv; + case QSOperator::InplaceAdd: return V4IR::OpAdd; + case QSOperator::InplaceLeftShift: return V4IR::OpLShift; + case QSOperator::InplaceMod: return V4IR::OpMod; + case QSOperator::InplaceMul: return V4IR::OpMul; + case QSOperator::InplaceOr: return V4IR::OpBitOr; + case QSOperator::InplaceRightShift: return V4IR::OpRShift; + case QSOperator::InplaceURightShift: return V4IR::OpURShift; + case QSOperator::InplaceXor: return V4IR::OpBitXor; + default: return V4IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(); + V4IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); + _block = iftrue; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(); + V4IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), endif, iffalse); + _block = iffalse; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + V4IR::Expr* left = *expression(ast->left); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: { + V4IR::Expr* right = *expression(ast->right); + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + break; + } + + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: { + V4IR::Expr* right = *expression(ast->right); + if (!left->isLValue()) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right, baseOp(ast->op)); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + if (_expr.accept(cx)) { + cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + } + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = call(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(); + V4IR::BasicBlock *endif = _function->newBasicBlock(); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + move(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + move(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *ast) +{ + V4IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + _expr.code = _block->CONST(V4IR::BoolType, 0); + return false; + } + if (_function->isStrict && expr->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + // Return values from calls are also not a reference, but we have to + // perform the call to allow for side effects. + if (expr->asCall()) { + _block->EXP(expr); + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 0); + } + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, ast->functionToken.startLine, ast->functionToken.startColumn); + _expr.code = _block->CLOSURE(function); + return false; +} + +V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) +{ + uint scope = 0; + Environment *e = _env; + V4IR::Function *f = _function; + + while (f && e->parent) { + if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) + break; + int index = e->findMember(name); + assert (index < e->members.size()); + if (index != -1) { + return _block->TEMP(index, scope); + } + const int argIdx = f->indexOfArgument(&name); + if (argIdx != -1) + return _block->TEMP(-(argIdx + 1), scope); + ++scope; + e = e->parent; + f = f->outer; + } + + if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && (!f || f->name != name)) + return _block->GLOBALNAME(name, line, col); + + // global context or with. Lookup by name + return _block->NAME(name, line, col); + +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(expr, args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), unop(V4IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + return false; +} + +bool Codegen::visit(NullExpression *) +{ + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(V4IR::NullType, 0); + + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::NumberType, ast->value); + } + return false; +} + +struct ObjectPropertyValue { + V4IR::Expr *value; + V4IR::Function *getter; + V4IR::Function *setter; +}; + +bool Codegen::visit(ObjectLiteral *ast) +{ + QMap<QString, ObjectPropertyValue> valueMap; + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(_block->NAME(QStringLiteral("Object"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn))); + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; + } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { + QString name = propertyName(gs->name); + V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, gs->getSetToken.startLine, gs->getSetToken.startColumn); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + if (gs->type == PropertyGetterSetter::Getter) + v.getter = function; + else + v.setter = function; + } else { + Q_UNREACHABLE(); + } + } + if (!valueMap.isEmpty()) { + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; + for (QMap<QString, ObjectPropertyValue>::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + V4IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + + if (it->value) { + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args)); + } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + current->expr = _block->TEMP(getter); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + } + + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + + if (_expr.accept(nx)) { + move(*expr, unop(V4IR::OpDecrement, *expr)); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + + if (_expr.accept(nx)) { + move(*expr, unop(V4IR::OpIncrement, *expr)); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + move(*expr, unop(V4IR::OpDecrement, *expr)); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + move(*expr, unop(V4IR::OpIncrement, *expr)); + if (_expr.accept(nx)) { + // nothing to do + } else { + _expr.code = *expr; + } + return false; +} + +bool Codegen::visit(RegExpLiteral *ast) +{ + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 1); + } + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(*expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); + _expr.code = _block->CONST(V4IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(FunctionDeclaration * /*ast*/) +{ + _expr.accept(nx); + return false; +} + +void Codegen::linearize(V4IR::Function *function) +{ + V4IR::BasicBlock *exitBlock = function->basicBlocks.last(); + assert(exitBlock->isTerminated()); + assert(exitBlock->terminator()->asRet()); + + QSet<V4IR::BasicBlock *> V; + V.insert(exitBlock); + + QVector<V4IR::BasicBlock *> trace; + + for (int i = 0; i < function->basicBlocks.size(); ++i) { + V4IR::BasicBlock *block = function->basicBlocks.at(i); + if (!block->isTerminated() && (i + 1) < function->basicBlocks.size()) { + V4IR::BasicBlock *next = function->basicBlocks.at(i + 1); + block->JUMP(next); + } + } + + struct I { static void trace(V4IR::BasicBlock *block, QSet<V4IR::BasicBlock *> *V, + QVector<V4IR::BasicBlock *> *output) { + if (block == 0 || V->contains(block)) + return; + + V->insert(block); + block->index = output->size(); + output->append(block); + + if (V4IR::Stmt *term = block->terminator()) { + if (V4IR::Jump *j = term->asJump()) { + trace(j->target, V, output); + } else if (V4IR::CJump *cj = term->asCJump()) { + if (! V->contains(cj->iffalse)) + trace(cj->iffalse, V, output); + else + trace(cj->iftrue, V, output); + } else if (V4IR::Try *t = term->asTry()) { + trace(t->tryBlock, V, output); + trace(t->catchBlock, V, output); + } + } + + // We could do this for each type above, but it is safer to have a + // "catchall" here + for (int ii = 0; ii < block->out.count(); ++ii) + trace(block->out.at(ii), V, output); + } + }; + + I::trace(function->basicBlocks.first(), &V, &trace); + + V.insert(exitBlock); + exitBlock->index = trace.size(); + trace.append(exitBlock); + + QVarLengthArray<V4IR::BasicBlock*> blocksToDelete; + foreach (V4IR::BasicBlock *b, function->basicBlocks) { + if (!V.contains(b)) { + foreach (V4IR::BasicBlock *out, b->out) { + int idx = out->in.indexOf(b); + if (idx >= 0) + out->in.remove(idx); + } + blocksToDelete.append(b); + } + } + qDeleteAll(blocksToDelete); + function->basicBlocks = trace; + + function->removeSharedExpressions(); + + if (qgetenv("NO_OPT").isEmpty()) + ConstantPropagation().run(function); + +#ifndef QV4_NO_LIVENESS + liveness(function); +#endif + + if (qgetenv("NO_OPT").isEmpty()) + removeDeadAssignments(function); + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector<V4IR::Stmt *> code; + QHash<V4IR::Stmt *, V4IR::BasicBlock *> leader; + + foreach (V4IR::BasicBlock *block, function->basicBlocks) { + leader.insert(block->statements.first(), block); + foreach (V4IR::Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + V4IR::Stmt *s = code.at(i); + + if (V4IR::BasicBlock *bb = leader.value(s)) { + qout << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (V4IR::BasicBlock *in, bb->in) + qout << " L" << in->index; + qout << endl; + } + V4IR::Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; + if (n && s->asJump() && s->asJump()->target == leader.value(n)) { + continue; + } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + s->dump(out, V4IR::Stmt::MIR); + out.flush(); + +#ifndef QV4_NO_LIVENESS + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + +# if 0 + if (! s->d->liveIn.isEmpty()) { + qout << " // lives in:"; + for (int i = 0; i < s->d->liveIn.size(); ++i) { + if (s->d->liveIn.testBit(i)) + qout << " %" << i; + } + } +# else + if (! s->d->liveOut.isEmpty()) { + qout << " // lives out:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } +# endif +#else + qout << " " << str; +#endif + + qout << endl; + + if (n && s->asCJump() && s->asCJump()->iffalse != leader.value(n)) { + qout << " goto L" << s->asCJump()->iffalse << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } + + //### NOTE: after this pass, the liveness information is not correct anymore! + if (qgetenv("NO_OPT").isEmpty()) + CompressTemps().run(function); +} + +V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) +{ + qSwap(_mode, mode); // enter function code. + + ScopeAndFinally *scopeAndFinally = 0; + + enterEnvironment(ast); + V4IR::Function *function = _module->newFunction(name, _function); + + if (_debugger) + _debugger->addFunction(function); + V4IR::BasicBlock *entryBlock = function->newBasicBlock(); + V4IR::BasicBlock *exitBlock = function->newBasicBlock(V4IR::Function::DontInsertBlock); + V4IR::BasicBlock *throwBlock = function->newBasicBlock(); + function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; + function->isNamedExpression = _env->isNamedFunctionExpression; + + // variables in global code are properties of the global context object, not locals as with other functions. + if (_mode == FunctionCode) { + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); + function->LOCAL(local); + unsigned t = entryBlock->newTemp(); + (*it).index = t; + } + } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, + static_cast<int>(tempIndex), 0 }; + _env->members.insert(inheritedLocal, member); + } + } + + V4IR::ExprList *args = 0; + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->CONST(V4IR::BoolType, mode == EvalCode); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args)); + } + } + + unsigned returnAddress = entryBlock->newTemp(); + + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); + exitBlock->RET(exitBlock->TEMP(returnAddress)); + V4IR::ExprList *throwArgs = function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(exitBlock); + Loop *loop = 0; + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + for (FormalParameterList *it = formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (_debugger) + _debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->TEMP(member.index), _block->CLOSURE(function)); + } + } + } + + sourceElements(body); + + _function->insertBasicBlock(_exitBlock); + + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + leaveEnvironment(); + + qSwap(_mode, mode); + + return function; +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *ast) +{ + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); + Loop *loop = 0; + if (ast->label.isEmpty()) + loop = _loop; + else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) + break; + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *ast) +{ + Loop *loop = 0; + if (ast->label.isEmpty()) { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) + break; + } + } else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + if (!loop->continueBlock) + loop = 0; + break; + } + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + Q_UNIMPLEMENTED(); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + V4IR::BasicBlock *loopbody = _function->newBasicBlock(); + V4IR::BasicBlock *loopcond = _function->newBasicBlock(); + V4IR::BasicBlock *loopend = _function->newBasicBlock(); + + enterLoop(ast, loopend, loopcond); + + _block->JUMP(loopbody); + + _block = loopbody; + statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; + condition(ast->expression, loopbody, loopend); + + _block = loopend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + if (_mode == EvalCode) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } + return false; +} + +bool Codegen::visit(ForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + int objectToIterateOn = _block->newTemp(); + move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(objectToIterateOn)); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(); + V4IR::BasicBlock *forbody = _function->newBasicBlock(); + V4IR::BasicBlock *forstep = _function->newBasicBlock(); + V4IR::BasicBlock *forend = _function->newBasicBlock(); + + enterLoop(ast, forend, forstep); + + statement(ast->initialiser); + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(); + V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock() : 0; + V4IR::BasicBlock *endif = _function->newBasicBlock(); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *ast) +{ + _labelledStatement = ast; + + if (AST::cast<AST::SwitchStatement *>(ast->statement) || + AST::cast<AST::WhileStatement *>(ast->statement) || + AST::cast<AST::DoWhileStatement *>(ast->statement) || + AST::cast<AST::ForStatement *>(ast->statement) || + AST::cast<AST::ForEachStatement *>(ast->statement) || + AST::cast<AST::LocalForStatement *>(ast->statement) || + AST::cast<AST::LocalForEachStatement *>(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + V4IR::BasicBlock *breakBlock = _function->newBasicBlock(); + enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + _block->JUMP(breakBlock); + _block = breakBlock; + leaveLoop(); + } + + return false; +} + +bool Codegen::visit(LocalForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(); + + enterLoop(ast, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(); + V4IR::BasicBlock *forbody = _function->newBasicBlock(); + V4IR::BasicBlock *forstep = _function->newBasicBlock(); + V4IR::BasicBlock *forend = _function->newBasicBlock(); + + enterLoop(ast, forend, forstep); + + variableDeclarationList(ast->declarations); + _block->JUMP(forcond); + + _block = forcond; + condition(ast->condition, forbody, forend); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (_mode != FunctionCode) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); + if (ast->expression) { + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + } + unwindException(0); + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *ast) +{ + V4IR::BasicBlock *switchend = _function->newBasicBlock(); + + if (ast->block) { + Result lhs = expression(ast->expression); + V4IR::BasicBlock *switchcond = _block; + + QHash<Node *, V4IR::BasicBlock *> blockMap; + + enterLoop(ast, switchend, 0); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + if (ast->block->defaultClause) { + _block = _function->newBasicBlock(); + blockMap[ast->block->defaultClause] = _block; + + for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(); + blockMap[clause] = _block; + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + } + + leaveLoop(); + + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + _block->JUMP(switchend); + + _block = switchend; + return false; +} + +bool Codegen::visit(ThrowStatement *ast) +{ + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); + return false; +} + +bool Codegen::visit(TryStatement *ast) +{ + _function->hasTry = true; + + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + + V4IR::BasicBlock *tryBody = _function->newBasicBlock(); + V4IR::BasicBlock *catchBody = _function->newBasicBlock(); + // We always need a finally body to clean up the exception handler + V4IR::BasicBlock *finallyBody = _function->newBasicBlock(); + + V4IR::BasicBlock *throwBlock = _function->newBasicBlock(); + V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(_returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(catchBody); + qSwap(_throwBlock, throwBlock); + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + + // Pass the hidden "needRethrow" TEMP to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + V4IR::ExprList *finishTryArgs = _function->New<V4IR::ExprList>(); + finishTryArgs->init(_block->TEMP(hasException)); + + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); + _scopeAndFinally = &tcf; + + int exception_to_rethrow = _block->newTemp(); + + _block->TRY(tryBody, catchBody, + ast->catchExpression ? ast->catchExpression->name.toString() : QString(), + _block->TEMP(exception_to_rethrow)); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody); + + _block = catchBody; + + if (ast->catchExpression) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + V4IR::BasicBlock *b = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), finallyBody, b); + _block = b; + } + + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true)); + + if (ast->catchExpression) { + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); + _scopeAndFinally = &scope; + statement(ast->catchExpression->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + } + _block->JUMP(finallyBody); + + _scopeAndFinally = tcf.parent; + + qSwap(_throwBlock, throwBlock); + + V4IR::BasicBlock *after = _function->newBasicBlock(); + _block = finallyBody; + + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); + + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + + _block = after; + + return false; +} + +void Codegen::unwindException(Codegen::ScopeAndFinally *outest) +{ + int savedDepthForWidthOrCatch = _function->insideWithOrCatch; + ScopeAndFinally *scopeAndFinally = _scopeAndFinally; + qSwap(_scopeAndFinally, scopeAndFinally); + while (_scopeAndFinally != outest) { + switch (_scopeAndFinally->type) { + case ScopeAndFinally::WithScope: + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); + // fall through + case ScopeAndFinally::CatchScope: + _scopeAndFinally = _scopeAndFinally->parent; + --_function->insideWithOrCatch; + break; + case ScopeAndFinally::TryScope: { + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); + ScopeAndFinally *tc = _scopeAndFinally; + _scopeAndFinally = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + break; + } + } + } + qSwap(_scopeAndFinally, scopeAndFinally); + _function->insideWithOrCatch = savedDepthForWidthOrCatch; +} + +bool Codegen::visit(VariableStatement *ast) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + V4IR::BasicBlock *whilecond = _function->newBasicBlock(); + V4IR::BasicBlock *whilebody = _function->newBasicBlock(); + V4IR::BasicBlock *whileend = _function->newBasicBlock(); + + enterLoop(ast, whileend, whilecond); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + _block->JUMP(whilecond); + + _block = whileend; + leaveLoop(); + + return false; +} + +bool Codegen::visit(WithStatement *ast) +{ + _function->hasWith = true; + + V4IR::BasicBlock *withBlock = _function->newBasicBlock(); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args)); + + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + + V4IR::BasicBlock *next = _function->newBasicBlock(); + _block->JUMP(next); + _block = next; + + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + assert(!"not implemented"); + return false; +} + +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + V4IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + VM::DiagnosticMessage *msg = new VM::DiagnosticMessage; + msg->fileName = _fileName; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = detail; + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); +} + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(VM::Value::fromString(_context, detail)); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/src/qml/qml/v4vm/qv4codegen_p.h b/src/qml/qml/v4vm/qv4codegen_p.h new file mode 100644 index 0000000000..031e75d207 --- /dev/null +++ b/src/qml/qml/v4vm/qv4codegen_p.h @@ -0,0 +1,444 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "qv4global.h" +#include "qv4jsir_p.h" +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <QtCore/QStringList> +#include <assert.h> + +namespace QQmlJS { + +namespace AST { +class UiParameterList; +} + +namespace VM { +struct DiagnosticMessage; +struct ExecutionContext; +} + +namespace Debugging { +class Debugger; +} // namespace Debugging + +class ErrorHandler +{ +public: + virtual void syntaxError(VM::DiagnosticMessage *message) = 0; +}; + +class Q_V4_EXPORT Codegen: protected AST::Visitor +{ +public: + Codegen(VM::ExecutionContext *ctx, bool strict); + Codegen(ErrorHandler *errorHandler, bool strictMode); + + enum Mode { + GlobalCode, + EvalCode, + FunctionCode + }; + + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::Program *ast, + V4IR::Module *module, + Mode mode = GlobalCode, + const QStringList &inheritedLocals = QStringList()); + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module); + +protected: + enum Format { ex, cx, nx }; + struct Result { + V4IR::Expr *code; + V4IR::BasicBlock *iftrue; + V4IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct Environment { + Environment *parent; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionExpression *function; + }; + typedef QMap<QString, Member> MemberMap; + + MemberMap members; + AST::FormalParameterList *formals; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; + bool isStrict; + bool isNamedFunctionExpression; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; + + Environment(Environment *parent) + : parent(parent) + , formals(0) + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) + , isStrict(false) + , isNamedFunctionExpression(false) + , usesArgumentsObject(ArgumentsObjectUnknown) + { + if (parent && parent->isStrict) + isStrict = true; + } + + int findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; + } + + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) + { + Environment *it = this; + *distance = 0; + for (; it; it = it->parent, ++(*distance)) { + int idx = it->findMember(name); + if (idx != -1) { + *scope = it; + *index = idx; + return true; + } + } + return false; + } + + void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + { + if (! name.isEmpty()) { + if (type != FunctionDefinition) { + for (AST::FormalParameterList *it = formals; it; it = it->next) + if (it->name == name) + return; + } + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.index = -1; + m.type = type; + m.function = function; + members.insert(name, m); + } else { + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } + }; + + Environment *newEnvironment(AST::Node *node, Environment *parent) + { + Environment *env = new Environment(parent); + _envMap.insert(node, env); + return env; + } + + struct UiMember { + }; + + struct ScopeAndFinally { + enum ScopeType { + WithScope, + TryScope, + CatchScope + }; + + ScopeAndFinally *parent; + AST::Finally *finally; + V4IR::ExprList *finishTryArgs; + ScopeType type; + + ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs) + : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope) + {} + }; + + struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; + V4IR::BasicBlock *breakBlock; + V4IR::BasicBlock *continueBlock; + Loop *parent; + ScopeAndFinally *scopeAndFinally; + + Loop(AST::Statement *node, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} + }; + + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + + void enterLoop(AST::Statement *node, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock); + void leaveLoop(); + + + V4IR::Expr *member(V4IR::Expr *base, const QString *name); + V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); + V4IR::Expr *argument(V4IR::Expr *expr); + V4IR::Expr *reference(V4IR::Expr *expr); + V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr); + V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right); + V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args); + void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid); + void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + + void linearize(V4IR::Function *function); + V4IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); + + void unwindException(ScopeAndFinally *outest); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *ast); + Result sourceElement(AST::SourceElement *ast); + UiMember uiObjectMember(AST::UiObjectMember *ast); + + void accept(AST::Node *node); + + void functionBody(AST::FunctionBody *ast); + void program(AST::Program *ast); + void sourceElements(AST::SourceElements *ast); + void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + +private: + QString _fileName; + Result _expr; + QString _property; + UiMember _uiMember; + V4IR::Module *_module; + V4IR::Function *_function; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_exitBlock; + V4IR::BasicBlock *_throwBlock; + unsigned _returnAddress; + Mode _mode; + Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; + ScopeAndFinally *_scopeAndFinally; + QHash<AST::Node *, Environment *> _envMap; + QHash<AST::FunctionExpression *, int> _functionMap; + VM::ExecutionContext *_context; + bool _strictMode; + Debugging::Debugger *_debugger; + ErrorHandler *_errorHandler; + + class ScanFunctions; +}; + +} // end of namespace QQmlJS + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/qml/v4vm/qv4context.cpp b/src/qml/qml/v4vm/qv4context.cpp new file mode 100644 index 0000000000..e1ce0016dd --- /dev/null +++ b/src/qml/qml/v4vm/qv4context.cpp @@ -0,0 +1,576 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QString> +#include "debugging.h" +#include <qv4context.h> +#include <qv4object.h> +#include <qv4objectproto.h> +#include "qv4mm.h" +#include <qv4argumentsobject.h> + +namespace QQmlJS { +namespace VM { + +DiagnosticMessage::DiagnosticMessage() + : offset(0) + , length(0) + , startLine(0) + , startColumn(0) + , type(0) + , next(0) +{} + +DiagnosticMessage::~DiagnosticMessage() +{ + delete next; +} + +String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const +{ + QString msg; + if (!fileName.isEmpty()) + msg = fileName + QLatin1Char(':'); + msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); + if (type == QQmlJS::VM::DiagnosticMessage::Error) + msg += QLatin1String("error"); + else + msg += QLatin1String("warning"); + msg += ": " + message; + + return ctx->engine->newString(msg); +} + +void ExecutionContext::createMutableBinding(String *name, bool deletable) +{ + + // find the right context to create the binding on + Object *activation = engine->globalObject; + ExecutionContext *ctx = this; + while (ctx) { + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + if (!c->activation) + c->activation = engine->newObject(); + activation = c->activation; + break; + } + ctx = ctx->outer; + } + + if (activation->__hasProperty__(name)) + return; + Property desc = Property::fromValue(Value::undefinedValue()); + PropertyAttributes attrs(Attr_Data); + attrs.setConfigurable(deletable); + activation->__defineOwnProperty__(this, name, desc, attrs); +} + +String * const *ExecutionContext::formals() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterList : 0; +} + +unsigned int ExecutionContext::formalCount() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterCount : 0; +} + +String * const *ExecutionContext::variables() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varList : 0; +} + +unsigned int ExecutionContext::variableCount() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varCount : 0; +} + + +void GlobalContext::init(ExecutionEngine *eng) +{ + type = Type_GlobalContext; + strictMode = false; + marked = false; + thisObject = Value::fromObject(eng->globalObject); + engine = eng; + outer = 0; + lookups = 0; + global = 0; +} + +void WithContext::init(ExecutionContext *p, Object *with) +{ + type = Type_WithContext; + strictMode = false; + marked = false; + thisObject = p->thisObject; + engine = p->engine; + outer = p; + lookups = p->lookups; + + withObject = with; +} + +void CatchContext::init(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue) +{ + type = Type_CatchContext; + strictMode = p->strictMode; + marked = false; + thisObject = p->thisObject; + engine = p->engine; + outer = p; + lookups = p->lookups; + + this->exceptionVarName = exceptionVarName; + this->exceptionValue = exceptionValue; +} + +void CallContext::initCallContext(ExecutionEngine *engine) +{ + type = Type_CallContext; + strictMode = function->strictMode; + marked = false; + this->engine = engine; + outer = function->scope; +#ifndef QT_NO_DEBUG + assert(outer->next != (ExecutionContext *)0x1); +#endif + + activation = 0; + + if (function->function) + lookups = function->function->lookups; + + uint argc = argumentCount; + + locals = (Value *)(this + 1); + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + + if (needsOwnArguments()) { + Value *args = arguments; + argumentCount = qMax(argc, function->formalParameterCount); + arguments = locals + function->varCount; + if (argc) + ::memcpy(arguments, args, argc * sizeof(Value)); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + + } + + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); + args->prototype = engine->objectPrototype; + Value arguments = Value::fromObject(args); + activation = engine->newObject(); + Property desc = Property::fromValue(Value::fromObject(args)); + activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable); + } +} + + +bool ExecutionContext::deleteProperty(String *name) +{ + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + hasWith = true; + WithContext *w = static_cast<WithContext *>(ctx); + if (w->withObject->__hasProperty__(name)) + return w->withObject->deleteProperty(this, name); + } else if (ctx->type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return false; + } else if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return false; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return false; + } + if (c->activation && c->activation->__hasProperty__(name)) + return c->activation->deleteProperty(this, name); + } else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + if (g->global->__hasProperty__(name)) + return g->global->deleteProperty(this, name); + } + } + + if (strictMode) + throwSyntaxError(0); + return true; +} + +bool CallContext::needsOwnArguments() const +{ + return function->needsActivation || argumentCount < function->formalParameterCount; +} + +void ExecutionContext::mark() +{ + if (marked) + return; + marked = true; + + if (type != Type_SimpleCallContext && outer) + outer->mark(); + + thisObject.mark(); + + if (type >= Type_SimpleCallContext) { + VM::CallContext *c = static_cast<CallContext *>(this); + for (unsigned arg = 0, lastArg = c->argumentCount; arg < lastArg; ++arg) + c->arguments[arg].mark(); + if (type >= Type_CallContext) { + for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) + c->locals[local].mark(); + if (c->activation) + c->activation->mark(); + c->function->mark(); + } + } else if (type == Type_WithContext) { + WithContext *w = static_cast<WithContext *>(this); + w->withObject->mark(); + } else if (type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(this); + if (c->exceptionVarName) + c->exceptionVarName->mark(); + c->exceptionValue.mark(); + } else if (type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(this); + g->global->mark(); + } +} + +void ExecutionContext::setProperty(String *name, const Value& value) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + if (w->__hasProperty__(name)) { + w->put(ctx, name, value); + return; + } + } else if (ctx->type == Type_CatchContext && static_cast<CatchContext *>(ctx)->exceptionVarName->isEqualTo(name)) { + static_cast<CatchContext *>(ctx)->exceptionValue = value; + return; + } else { + Object *activation = 0; + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + for (unsigned int i = 0; i < c->function->varCount; ++i) + if (c->function->varList[i]->isEqualTo(name)) { + c->locals[i] = value; + return; + } + for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i) + if (c->function->formalParameterList[i]->isEqualTo(name)) { + c->arguments[i] = value; + return; + } + activation = c->activation; + } else if (ctx->type == Type_GlobalContext) { + activation = static_cast<GlobalContext *>(ctx)->global; + } + + if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) { + activation->put(this, name, value); + return; + } + } + } + if (strictMode || name->isEqualTo(engine->id_this)) + throwReferenceError(Value::fromString(name)); + engine->globalObject->put(this, name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + name->makeIdentifier(this); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(ctx, name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + VM::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(c, name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(g, name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyNoThrow(String *name) +{ + name->makeIdentifier(this); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(ctx, name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + VM::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(c, name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(g, name, &hasProperty); + if (hasProperty) + return v; + } + } + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyAndBase(String *name, Object **base) +{ + *base = 0; + name->makeIdentifier(this); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(ctx, name, &hasProperty); + if (hasProperty) { + *base = w; + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + VM::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(c, name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(g, name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + +void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) +{ + Value lhs = getProperty(name); + Value result; + op(this, &result, lhs, value); + setProperty(name, result); +} + +void ExecutionContext::throwError(const Value &value) +{ + __qmljs_builtin_throw(this, value); +} + +void ExecutionContext::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) +{ + throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); +} + +void ExecutionContext::throwTypeError() +{ + throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error")))); +} + +void ExecutionContext::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwReferenceError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg))); +} + +void ExecutionContext::throwRangeError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" out of range"); + throwError(Value::fromObject(engine->newRangeErrorObject(this, msg))); +} + +void ExecutionContext::throwURIError(Value msg) +{ + throwError(Value::fromObject(engine->newURIErrorObject(this, msg))); +} + +} // namespace VM +} // namespace QQmlJS diff --git a/src/qml/qml/v4vm/qv4context.h b/src/qml/qml/v4vm/qv4context.h new file mode 100644 index 0000000000..c26cc6bfc5 --- /dev/null +++ b/src/qml/qml/v4vm/qv4context.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENVIRONMENT_H +#define QMLJS_ENVIRONMENT_H + +#include "qv4global.h" +#include <qv4runtime.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Object; +struct ExecutionEngine; +struct DeclarativeEnvironment; +struct Lookup; + +struct Q_V4_EXPORT DiagnosticMessage +{ + enum { Error, Warning }; + + QString fileName; + quint32 offset; + quint32 length; + quint32 startLine; + unsigned startColumn: 31; + unsigned type: 1; + QString message; + DiagnosticMessage *next; + + DiagnosticMessage(); + ~DiagnosticMessage(); + String *buildFullMessage(ExecutionContext *ctx) const; +}; + +struct CallContext; + +struct ExecutionContext +{ + enum Type { + Type_GlobalContext = 0x1, + Type_CatchContext = 0x2, + Type_WithContext = 0x3, + Type_SimpleCallContext = 0x4, + Type_CallContext = 0x5, + Type_QmlContext = 0x6 + }; + + Type type; + bool strictMode; + bool marked; + + Value thisObject; + + ExecutionEngine *engine; + ExecutionContext *parent; + ExecutionContext *outer; + Lookup *lookups; + ExecutionContext *next; // used in the GC + + String * const *formals() const; + unsigned int formalCount() const; + String * const *variables() const; + unsigned int variableCount() const; + + void createMutableBinding(String *name, bool deletable); + + void Q_NORETURN throwError(const Value &value); + void Q_NORETURN throwError(const QString &message); + void Q_NORETURN throwSyntaxError(DiagnosticMessage *message); + void Q_NORETURN throwTypeError(); + void Q_NORETURN throwReferenceError(Value value); + void Q_NORETURN throwRangeError(Value value); + void Q_NORETURN throwURIError(Value msg); + void Q_NORETURN throwUnimplemented(const QString &message); + + void setProperty(String *name, const Value &value); + Value getProperty(String *name); + Value getPropertyNoThrow(String *name); + Value getPropertyAndBase(String *name, Object **base); + void inplaceBitOp(String *name, const QQmlJS::VM::Value &value, BinOp op); + bool deleteProperty(String *name); + + inline Value argument(unsigned int index = 0); + + void mark(); + + inline CallContext *asCallContext(); +}; + +struct SimpleCallContext : public ExecutionContext +{ + FunctionObject *function; + Value *arguments; + unsigned int argumentCount; +}; + +struct CallContext : public SimpleCallContext +{ + void initCallContext(QQmlJS::VM::ExecutionEngine *engine); + bool needsOwnArguments() const; + + Value *locals; + Object *activation; +}; + +struct GlobalContext : public ExecutionContext +{ + void init(ExecutionEngine *e); + + Object *global; +}; + +struct CatchContext : public ExecutionContext +{ + void init(ExecutionContext *p, String *exceptionVarName, const QQmlJS::VM::Value &exceptionValue); + + String *exceptionVarName; + Value exceptionValue; +}; + +struct WithContext : public ExecutionContext +{ + Object *withObject; + + void init(ExecutionContext *p, Object *with); +}; + +inline Value ExecutionContext::argument(unsigned int index) +{ + if (type >= Type_SimpleCallContext) { + CallContext *ctx = static_cast<CallContext *>(this); + if (index < ctx->argumentCount) + return ctx->arguments[index]; + } + return Value::undefinedValue(); +} + +inline CallContext *ExecutionContext::asCallContext() +{ + return type >= Type_CallContext ? static_cast<CallContext *>(this) : 0; +} + +/* Function *f, int argc */ +#define requiredMemoryForExecutionContect(f, argc) \ + sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) +#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value)) + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4dateobject.cpp b/src/qml/qml/v4vm/qv4dateobject.cpp new file mode 100644 index 0000000000..adeb11f862 --- /dev/null +++ b/src/qml/qml/v4vm/qv4dateobject.cpp @@ -0,0 +1,1316 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4dateobject.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> +#include <time.h> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#include <wtf/MathExtras.h> + +#ifdef Q_OS_WIN +# include <windows.h> +#else +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#endif + +using namespace QQmlJS::VM; + +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + assert(x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double d = DayFromYear(year); + bool leap = InLeapYear(d*msPerDay); + + d += DayFromMonth(month, leap); + d += day - 1; + + return d; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ + struct tm tmtm; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + __time64_t tt = (__time64_t)(t / msPerSecond); + if (!_localtime64_s(&tmtm, &tt)) +#else + long int tt = (long int)(t / msPerSecond); + if (!localtime_r((const time_t*) &tt, &tmtm)) +#endif + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_OS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double FromDateTime(const QDateTime &dt) +{ + if (!dt.isValid()) + return qSNaN(); + QDate date = dt.date(); + QTime taim = dt.time(); + int year = date.year(); + int month = date.month() - 1; + int day = date.day(); + int hours = taim.hour(); + int mins = taim.minute(); + int secs = taim.second(); + int ms = taim.msec(); + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + if (dt.timeSpec() == Qt::LocalTime) + t = UTC(t); + return TimeClip(t); +} + +static inline double ParseString(const QString &s) +{ + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t += offset * offsetSign * 60 * 1000; + return t; + } + + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + return FromDateTime(dt); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (isnan(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (isnan(t)) + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (isnan(t)) + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_OS_WIN + struct tm t; + time_t curr; + time(&curr); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + +DEFINE_MANAGED_VTABLE(DateCtor); + +DateCtor::DateCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value DateCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + double t = 0; + + if (argc == 0) + t = currentTime(); + + else if (argc == 1) { + Value arg = args[0]; + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + arg = __qmljs_to_primitive(arg, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.stringValue()->toQString()); + else + t = TimeClip(arg.toNumber()); + } + + else { // argc > 1 + double year = args[0].toNumber(); + double month = args[1].toNumber(); + double day = argc >= 3 ? args[2].toNumber() : 1; + double hours = argc >= 4 ? args[3].toNumber() : 0; + double mins = argc >= 5 ? args[4].toNumber() : 0; + double secs = argc >= 6 ? args[5].toNumber() : 0; + double ms = argc >= 7 ? args[6].toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + + Object *d = ctx->engine->newDateObject(Value::fromDouble(t)); + return Value::fromObject(d); +} + +Value DateCtor::call(Managed *, ExecutionContext *ctx, const Value &, Value *, int) +{ + double t = currentTime(); + return Value::fromString(ctx, ToString(t)); +} + +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); + LocalTZA = getLocalTZA(); + + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); + defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); +} + +double DatePrototype::getThisDate(ExecutionContext *ctx) +{ + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.asDouble(); + else { + ctx->throwTypeError(); + return 0; + } +} + +Value DatePrototype::method_parse(SimpleCallContext *ctx) +{ + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +Value DatePrototype::method_UTC(SimpleCallContext *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(); + double month = ctx->argument(1).toNumber(); + double day = numArgs >= 3 ? ctx->argument(2).toNumber() : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber() : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber() : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber() : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + return Value::fromDouble(TimeClip(t)); + } + return Value::undefinedValue(); +} + +Value DatePrototype::method_now(SimpleCallContext *ctx) +{ + Q_UNUSED(ctx); + double t = currentTime(); + return Value::fromDouble(t); +} + +Value DatePrototype::method_toString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToString(t)); +} + +Value DatePrototype::method_toDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToDateString(t)); +} + +Value DatePrototype::method_toTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToTimeString(t)); +} + +Value DatePrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleString(t)); +} + +Value DatePrototype::method_toLocaleDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleDateString(t)); +} + +Value DatePrototype::method_toLocaleTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleTimeString(t)); +} + +Value DatePrototype::method_valueOf(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTime(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = YearFromTime(LocalTime(t)) - 1900; + return Value::fromDouble(t); +} + +Value DatePrototype::method_getFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = YearFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = YearFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = MonthFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = MonthFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = DateFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = DateFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = WeekDay(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = WeekDay(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = HourFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = HourFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = MinFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = MinFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = SecFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = SecFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = msFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = msFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! isnan(t)) + t = (t - LocalTime(t)) / msPerMinute; + return Value::fromDouble(t); +} + +Value DatePrototype::method_setTime(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber())); + return self->value; +} + +Value DatePrototype::method_setMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setUTCMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (isnan(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(); + double r; + if (isnan(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.setDouble(r); + return self->value; +} + +Value DatePrototype::method_setUTCFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + if (isnan(t)) + t = 0; + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_toUTCString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); +} + +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + +Value DatePrototype::method_toJSON(SimpleCallContext *ctx) +{ + Value O = __qmljs_to_object(ctx, ctx->thisObject); + Value tv = __qmljs_to_primitive(O, NUMBER_HINT); + + if (tv.isNumber() && !std::isfinite(tv.toNumber())) + return Value::nullValue(); + + FunctionObject *toIso = O.objectValue()->get(ctx, ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); + + if (!toIso) + ctx->throwTypeError(); + + return toIso->call(ctx, ctx->thisObject, 0, 0); +} diff --git a/src/qml/qml/v4vm/qv4dateobject.h b/src/qml/qml/v4vm/qv4dateobject.h new file mode 100644 index 0000000000..49a879e809 --- /dev/null +++ b/src/qml/qml/v4vm/qv4dateobject.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATEOBJECT_P_H +#define QV4DATEOBJECT_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct DateObject: Object { + Value value; + DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; } +}; + +struct DateCtor: FunctionObject +{ + DateCtor(ExecutionContext *scope); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct DatePrototype: DateObject +{ + DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_UTC(SimpleCallContext *ctx); + static Value method_now(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toDateString(SimpleCallContext *ctx); + static Value method_toTimeString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_toLocaleDateString(SimpleCallContext *ctx); + static Value method_toLocaleTimeString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_getTime(SimpleCallContext *ctx); + static Value method_getYear(SimpleCallContext *ctx); + static Value method_getFullYear(SimpleCallContext *ctx); + static Value method_getUTCFullYear(SimpleCallContext *ctx); + static Value method_getMonth(SimpleCallContext *ctx); + static Value method_getUTCMonth(SimpleCallContext *ctx); + static Value method_getDate(SimpleCallContext *ctx); + static Value method_getUTCDate(SimpleCallContext *ctx); + static Value method_getDay(SimpleCallContext *ctx); + static Value method_getUTCDay(SimpleCallContext *ctx); + static Value method_getHours(SimpleCallContext *ctx); + static Value method_getUTCHours(SimpleCallContext *ctx); + static Value method_getMinutes(SimpleCallContext *ctx); + static Value method_getUTCMinutes(SimpleCallContext *ctx); + static Value method_getSeconds(SimpleCallContext *ctx); + static Value method_getUTCSeconds(SimpleCallContext *ctx); + static Value method_getMilliseconds(SimpleCallContext *ctx); + static Value method_getUTCMilliseconds(SimpleCallContext *ctx); + static Value method_getTimezoneOffset(SimpleCallContext *ctx); + static Value method_setTime(SimpleCallContext *ctx); + static Value method_setMilliseconds(SimpleCallContext *ctx); + static Value method_setUTCMilliseconds(SimpleCallContext *ctx); + static Value method_setSeconds(SimpleCallContext *ctx); + static Value method_setUTCSeconds(SimpleCallContext *ctx); + static Value method_setMinutes(SimpleCallContext *ctx); + static Value method_setUTCMinutes(SimpleCallContext *ctx); + static Value method_setHours(SimpleCallContext *ctx); + static Value method_setUTCHours(SimpleCallContext *ctx); + static Value method_setDate(SimpleCallContext *ctx); + static Value method_setUTCDate(SimpleCallContext *ctx); + static Value method_setMonth(SimpleCallContext *ctx); + static Value method_setUTCMonth(SimpleCallContext *ctx); + static Value method_setYear(SimpleCallContext *ctx); + static Value method_setFullYear(SimpleCallContext *ctx); + static Value method_setUTCFullYear(SimpleCallContext *ctx); + static Value method_toUTCString(SimpleCallContext *ctx); + static Value method_toISOString(SimpleCallContext *ctx); + static Value method_toJSON(SimpleCallContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4engine.cpp b/src/qml/qml/v4vm/qv4engine.cpp new file mode 100644 index 0000000000..7d5c79f333 --- /dev/null +++ b/src/qml/qml/v4vm/qv4engine.cpp @@ -0,0 +1,551 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4engine.h> +#include <qv4value.h> +#include <qv4object.h> +#include <qv4objectproto.h> +#include <qv4arrayobject.h> +#include <qv4booleanobject.h> +#include <qv4globalobject.h> +#include <qv4errorobject.h> +#include <qv4functionobject.h> +#include <qv4mathobject.h> +#include <qv4numberobject.h> +#include <qv4regexpobject.h> +#include <qv4runtime.h> +#include "qv4mm.h" +#include <qv4argumentsobject.h> +#include <qv4dateobject.h> +#include <qv4jsonobject.h> +#include <qv4stringobject.h> +#include <qv4identifier.h> +#include <qv4unwindhelper.h> +#include "qv4isel_masm_p.h" +#include "debugging.h" +#include "qv4executableallocator.h" + +namespace QQmlJS { +namespace VM { + +ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) + : memoryManager(new QQmlJS::VM::MemoryManager) + , executableAllocator(new QQmlJS::VM::ExecutableAllocator) + , debugger(0) + , globalObject(0) + , globalCode(0) + , externalResourceComparison(0) + , regExpCache(0) +{ + MemoryManager::GCBlocker gcBlocker(memoryManager); + + if (!factory) + factory = new MASM::ISelFactory; + iselFactory.reset(factory); + + memoryManager->setExecutionEngine(this); + + identifierCache = new Identifiers(this); + + id_undefined = newIdentifier(QStringLiteral("undefined")); + id_null = newIdentifier(QStringLiteral("null")); + id_true = newIdentifier(QStringLiteral("true")); + id_false = newIdentifier(QStringLiteral("false")); + id_boolean = newIdentifier(QStringLiteral("boolean")); + id_number = newIdentifier(QStringLiteral("number")); + id_string = newIdentifier(QStringLiteral("string")); + id_object = newIdentifier(QStringLiteral("object")); + id_function = newIdentifier(QStringLiteral("function")); + id_length = newIdentifier(QStringLiteral("length")); + id_prototype = newIdentifier(QStringLiteral("prototype")); + id_constructor = newIdentifier(QStringLiteral("constructor")); + id_arguments = newIdentifier(QStringLiteral("arguments")); + id_caller = newIdentifier(QStringLiteral("caller")); + id_this = newIdentifier(QStringLiteral("this")); + id___proto__ = newIdentifier(QStringLiteral("__proto__")); + id_enumerable = newIdentifier(QStringLiteral("enumerable")); + id_configurable = newIdentifier(QStringLiteral("configurable")); + id_writable = newIdentifier(QStringLiteral("writable")); + id_value = newIdentifier(QStringLiteral("value")); + id_get = newIdentifier(QStringLiteral("get")); + id_set = newIdentifier(QStringLiteral("set")); + id_eval = newIdentifier(QStringLiteral("eval")); + + emptyClass = new InternalClass(this); + arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); + initRootContext(); + + objectPrototype = new (memoryManager) ObjectPrototype(this); + stringPrototype = new (memoryManager) StringPrototype(rootContext); + numberPrototype = new (memoryManager) NumberPrototype(this); + booleanPrototype = new (memoryManager) BooleanPrototype(this); + arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); + datePrototype = new (memoryManager) DatePrototype(this); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(this); + errorPrototype = new (memoryManager) ErrorPrototype(rootContext); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(rootContext); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(rootContext); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(rootContext); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(rootContext); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(rootContext); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(rootContext); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = objectPrototype; + rangeErrorPrototype->prototype = objectPrototype; + referenceErrorPrototype->prototype = objectPrototype; + syntaxErrorPrototype->prototype = objectPrototype; + typeErrorPrototype->prototype = objectPrototype; + uRIErrorPrototype->prototype = objectPrototype; + + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); + + objectCtor.objectValue()->prototype = functionPrototype; + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = functionPrototype; + rangeErrorCtor.objectValue()->prototype = functionPrototype; + referenceErrorCtor.objectValue()->prototype = functionPrototype; + syntaxErrorCtor.objectValue()->prototype = functionPrototype; + typeErrorCtor.objectValue()->prototype = functionPrototype; + uRIErrorCtor.objectValue()->prototype = functionPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(rootContext, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(rootContext, errorCtor); + evalErrorPrototype->init(rootContext, evalErrorCtor); + rangeErrorPrototype->init(rootContext, rangeErrorCtor); + referenceErrorPrototype->init(rootContext, referenceErrorCtor); + syntaxErrorPrototype->init(rootContext, syntaxErrorCtor); + typeErrorPrototype->init(rootContext, typeErrorCtor); + uRIErrorPrototype->init(rootContext, uRIErrorCtor); + + // + // set up the global object + // + globalObject = newObject(/*rootContext*/); + rootContext->global = globalObject; + rootContext->thisObject = Value::fromObject(globalObject); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext))); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); + + globalObject->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); + globalObject->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN())); + globalObject->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); + + evalFunction = new (memoryManager) EvalFunction(rootContext); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); +} + +ExecutionEngine::~ExecutionEngine() +{ + delete regExpCache; + UnwindHelper::deregisterFunctions(functions); + qDeleteAll(functions); + delete memoryManager; + delete executableAllocator; +} + +void ExecutionEngine::initRootContext() +{ + rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); + current = rootContext; + current->parent = 0; + rootContext->init(this); +} + +WithContext *ExecutionEngine::newWithContext(Object *with) +{ + ExecutionContext *p = current; + WithContext *w = static_cast<WithContext *>(memoryManager->allocContext(sizeof(WithContext))); + w->parent = current; + current = w; + + w->init(p, with); + return w; +} + +CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue) +{ + ExecutionContext *p = current; + CatchContext *c = static_cast<CatchContext *>(memoryManager->allocContext(sizeof(CatchContext))); + c->parent = current; + current = c; + + c->init(p, exceptionVarName, exceptionValue); + return c; +} + +CallContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c = static_cast<CallContext *>(memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc))); + c->parent = current; + current = c; + + c->function = f; + c->thisObject = thisObject; + c->arguments = args; + c->argumentCount = argc; + c->initCallContext(this); + + return c; +} + +CallContext *ExecutionEngine::newCallContext(void *stackSpace, FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c; + uint memory = requiredMemoryForExecutionContect(f, argc); + if (f->needsActivation || memory > stackContextSize) { + c = static_cast<CallContext *>(memoryManager->allocContext(memory)); + } else { + c = (CallContext *)stackSpace; +#ifndef QT_NO_DEBUG + c->next = (CallContext *)0x1; +#endif + } + c->parent = current; + current = c; + + c->function = f; + c->thisObject = thisObject; + c->arguments = args; + c->argumentCount = argc; + c->initCallContext(this); + + return c; +} + + +ExecutionContext *ExecutionEngine::pushGlobalContext() +{ + GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); + *g = *rootContext; + g->parent = current; + current = g; + + return current; +} + +Function *ExecutionEngine::newFunction(const QString &name) +{ + VM::Function *f = new VM::Function(newIdentifier(name)); + functions.append(f); + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) +{ + BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, VM::Function *function) +{ + assert(function); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); + return f; +} + +BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs) +{ + assert(target); + + BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); + return f; +} + + +Object *ExecutionEngine::newObject() +{ + Object *object = new (memoryManager) Object(this); + object->prototype = objectPrototype; + return object; +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new (memoryManager) String(s); +} + +String *ExecutionEngine::newIdentifier(const QString &text) +{ + return identifierCache->insert(text); +} + +Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &value) +{ + StringObject *object = new (memoryManager) StringObject(ctx, value); + object->prototype = stringPrototype; + return object; +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + NumberObject *object = new (memoryManager) NumberObject(this, value); + object->prototype = numberPrototype; + return object; +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + Object *object = new (memoryManager) BooleanObject(this, value); + object->prototype = booleanPrototype; + return object; +} + +Object *ExecutionEngine::newFunctionObject(ExecutionContext *ctx) +{ + Object *object = new (memoryManager) FunctionObject(ctx); + object->prototype = functionPrototype; + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx) +{ + ArrayObject *object = new (memoryManager) ArrayObject(ctx); + object->prototype = arrayPrototype; + return object; +} + +Object *ExecutionEngine::newDateObject(const Value &value) +{ + Object *object = new (memoryManager) DateObject(this, value); + object->prototype = datePrototype; + return object; +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +{ + bool global = (flags & V4IR::RegExp::RegExp_Global); + bool ignoreCase = false; + bool multiline = false; + if (flags & V4IR::RegExp::RegExp_IgnoreCase) + ignoreCase = true; + if (flags & V4IR::RegExp::RegExp_Multiline) + multiline = true; + + return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); +} + +RegExpObject *ExecutionEngine::newRegExpObject(RegExp* re, bool global) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); + object->prototype = regExpPrototype; + return object; +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + ErrorObject *object = new (memoryManager) ErrorObject(rootContext, value); + object->prototype = errorPrototype; + return object; +} + +Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) +{ + return new (memoryManager) SyntaxErrorObject(ctx, message); +} + +Object *ExecutionEngine::newReferenceErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) ReferenceErrorObject(ctx, message); +} + +Object *ExecutionEngine::newTypeErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) TypeErrorObject(ctx, message); +} + +Object *ExecutionEngine::newRangeErrorObject(ExecutionContext *ctx, const QString &message) +{ + return new (memoryManager) RangeErrorObject(ctx, message); +} + +Object *ExecutionEngine::newURIErrorObject(ExecutionContext *ctx, Value message) +{ + return new (memoryManager) URIErrorObject(ctx, message); +} + +Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) +{ + return new (memoryManager) ForEachIteratorObject(ctx, o); +} + +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + Property pd = Property::fromAccessor(get, set); + argumentsAccessors[i] = pd; + } +} + +void ExecutionEngine::markObjects() +{ + identifierCache->mark(); + + globalObject->mark(); + + if (globalCode) + globalCode->mark(); + + for (int i = 0; i < argumentsAccessors.size(); ++i) { + const Property &pd = argumentsAccessors.at(i); + pd.getter()->mark(); + pd.setter()->mark(); + } + + ExecutionContext *c = current; + while (c) { + c->mark(); + c = c->parent; + } + + for (int i = 0; i < functions.size(); ++i) + functions.at(i)->mark(); + + id_length->mark(); + id_prototype->mark(); + id_constructor->mark(); + id_arguments->mark(); + id_caller->mark(); + id_this->mark(); + id___proto__->mark(); + id_enumerable->mark(); + id_configurable->mark(); + id_writable->mark(); + id_value->mark(); + id_get->mark(); + id_set->mark(); + id_eval->mark(); +} + +Value ExecutionEngine::run(Function *function, ExecutionContext *ctx) +{ + if (!ctx) + ctx = rootContext; + + TemporaryAssignment<Function*>(globalCode, function); + + // ### Would be better to have a SavedExecutionState object that + // saves this and restores it in the destructor (to survive an exception). + ctx->strictMode = function->isStrict; + ctx->lookups = function->lookups; + + if (debugger) + debugger->aboutToCall(0, ctx); + QQmlJS::VM::Value result = function->code(ctx, function->codeData); + if (debugger) + debugger->justLeft(ctx); + return result; +} + +} // namespace VM +} // namespace QQmlJS diff --git a/src/qml/qml/v4vm/qv4engine.h b/src/qml/qml/v4vm/qv4engine.h new file mode 100644 index 0000000000..538796eefa --- /dev/null +++ b/src/qml/qml/v4vm/qv4engine.h @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ENGINE_H +#define QV4ENGINE_H + +#include "qv4global.h" +#include "qv4isel_p.h" +#include "qv4object.h" +#include "qv4util.h" +#include "qv4context.h" +#include "qv4property.h" +#include <setjmp.h> + +#include <wtf/BumpPointerAllocator.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +namespace Debugging { +class Debugger; +} // namespace Debugging + +namespace VM { + +struct Value; +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct BoundFunction; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; +class UnwindHelper; +class ExecutableAllocator; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct EvalFunction; +struct Identifiers; +struct InternalClass; + +class RegExp; +class RegExpCache; + +typedef bool (*ExternalResourceComparison)(const VM::Value &a, const VM::Value &b); + +struct Q_V4_EXPORT ExecutionEngine +{ + MemoryManager *memoryManager; + ExecutableAllocator *executableAllocator; + QScopedPointer<EvalISelFactory> iselFactory; + + ExecutionContext *current; + GlobalContext *rootContext; + + WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine. + + Identifiers *identifierCache; + + Debugging::Debugger *debugger; + + Object *globalObject; + + VM::Function *globalCode; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; + + InternalClass *emptyClass; + InternalClass *arrayClass; + + EvalFunction *evalFunction; + + QVector<Property> argumentsAccessors; + + String *id_undefined; + String *id_null; + String *id_true; + String *id_false; + String *id_boolean; + String *id_number; + String *id_string; + String *id_object; + String *id_function; + String *id_length; + String *id_prototype; + String *id_constructor; + String *id_arguments; + String *id_caller; + String *id_this; + String *id___proto__; + String *id_enumerable; + String *id_configurable; + String *id_writable; + String *id_value; + String *id_get; + String *id_set; + String *id_eval; + + QVector<Function *> functions; + + ExternalResourceComparison externalResourceComparison; + + RegExpCache *regExpCache; + + ExecutionEngine(EvalISelFactory *iselFactory = 0); + ~ExecutionEngine(); + + WithContext *newWithContext(Object *with); + CatchContext *newCatchContext(String* exceptionVarName, const QQmlJS::VM::Value &exceptionValue); + CallContext *newCallContext(FunctionObject *f, const QQmlJS::VM::Value &thisObject, QQmlJS::VM::Value *args, int argc); + CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QQmlJS::VM::Value &thisObject, QQmlJS::VM::Value *args, int argc); + ExecutionContext *pushGlobalContext(); + void pushContext(SimpleCallContext *context); + ExecutionContext *popContext(); + + VM::Function *newFunction(const QString &name); + + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + FunctionObject *newScriptFunction(ExecutionContext *scope, VM::Function *function); + BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs); + + Object *newObject(); + + String *newString(const QString &s); + String *newIdentifier(const QString &text); + + Object *newStringObject(ExecutionContext *ctx, const Value &value); + Object *newNumberObject(const Value &value); + Object *newBooleanObject(const Value &value); + Object *newFunctionObject(ExecutionContext *ctx); + + ArrayObject *newArrayObject(ExecutionContext *ctx); + + Object *newDateObject(const Value &value); + + RegExpObject *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(RegExp* re, bool global); + + Object *newErrorObject(const Value &value); + Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); + Object *newReferenceErrorObject(ExecutionContext *ctx, const QString &message); + Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newRangeErrorObject(ExecutionContext *ctx, const QString &message); + Object *newURIErrorObject(ExecutionContext *ctx, Value message); + + Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + void requireArgumentsAccessors(int n); + + void markObjects(); + + Value run(VM::Function *function, ExecutionContext *ctx = 0); + + void initRootContext(); +}; + +inline void ExecutionEngine::pushContext(SimpleCallContext *context) +{ + context->parent = current; + current = context; +} + +inline ExecutionContext *ExecutionEngine::popContext() +{ + CallContext *c = current->asCallContext(); + if (c && !c->needsOwnArguments()) { + c->arguments = 0; + c->argumentCount = 0; + } + + current = current->parent; + return current; +} + + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ENGINE_H diff --git a/src/qml/qml/v4vm/qv4errorobject.cpp b/src/qml/qml/v4vm/qv4errorobject.cpp new file mode 100644 index 0000000000..b69262e77c --- /dev/null +++ b/src/qml/qml/v4vm/qv4errorobject.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4errorobject.h" +#include "qv4mm.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QQmlJS::VM; + +ErrorObject::ErrorObject(ExecutionContext *context, const Value &message, ErrorType t) + : Object(context->engine) +{ + type = Type_ErrorObject; + subtype = t; + + if (!message.isUndefined()) + defineDefaultProperty(context->engine->newString(QStringLiteral("message")), message); + defineDefaultProperty(context, QLatin1String("name"), Value::fromString(context, className())); +} + +DEFINE_MANAGED_VTABLE(SyntaxErrorObject); + +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError) + , msg(message) +{ + vtbl = &static_vtbl; + prototype = ctx->engine->syntaxErrorPrototype; +} + + + +EvalErrorObject::EvalErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx, message, EvalError) +{ + prototype = ctx->engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx, message, RangeError) +{ + prototype = ctx->engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx, Value::fromString(ctx,message), RangeError) +{ + prototype = ctx->engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx, message, ReferenceError) +{ + prototype = ctx->engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx, Value::fromString(ctx,message), ReferenceError) +{ + prototype = ctx->engine->referenceErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx, message, TypeError) +{ + prototype = ctx->engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionContext *ctx, const QString &message) + : ErrorObject(ctx, Value::fromString(ctx,message), TypeError) +{ + prototype = ctx->engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionContext *ctx, const Value &message) + : ErrorObject(ctx, message, URIError) +{ + prototype = ctx->engine->uRIErrorPrototype; +} + +DEFINE_MANAGED_VTABLE(ErrorCtor); +DEFINE_MANAGED_VTABLE(EvalErrorCtor); +DEFINE_MANAGED_VTABLE(RangeErrorCtor); +DEFINE_MANAGED_VTABLE(ReferenceErrorCtor); +DEFINE_MANAGED_VTABLE(SyntaxErrorCtor); +DEFINE_MANAGED_VTABLE(TypeErrorCtor); +DEFINE_MANAGED_VTABLE(URIErrorCtor); + +ErrorCtor::ErrorCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value ErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(ctx->engine->newErrorObject(argc ? args[0] : Value::undefinedValue())); +} + +Value ErrorCtor::call(Managed *that, ExecutionContext *ctx, const Value &, Value *args, int argc) +{ + return that->construct(ctx, args, argc); +} + +Value EvalErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(new (ctx->engine->memoryManager) EvalErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); +} + +Value RangeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(new (ctx->engine->memoryManager) RangeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); +} + +Value ReferenceErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(new (ctx->engine->memoryManager) ReferenceErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); +} + +Value SyntaxErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *, int) +{ + return Value::fromObject(new (ctx->engine->memoryManager) SyntaxErrorObject(ctx, 0)); +} + +Value TypeErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(new (ctx->engine->memoryManager) TypeErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); +} + +Value URIErrorCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + return Value::fromObject(new (ctx->engine->memoryManager) URIErrorObject(ctx, argc ? args[0] : Value::undefinedValue())); +} + +void ErrorPrototype::init(ExecutionContext *ctx, const Value &ctor, Object *obj) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + obj->defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(ctx, QStringLiteral("message"), Value::fromString(ctx, QString())); +} + +Value ErrorPrototype::method_toString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + Value name = o->get(ctx, ctx->engine->newString(QString::fromLatin1("name"))); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + Value message = o->get(ctx, ctx->engine->newString(QString::fromLatin1("message"))); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + return Value::fromString(ctx, str); +} diff --git a/src/qml/qml/v4vm/qv4errorobject.h b/src/qml/qml/v4vm/qv4errorobject.h new file mode 100644 index 0000000000..20a20ad980 --- /dev/null +++ b/src/qml/qml/v4vm/qv4errorobject.h @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ERROROBJECT_H +#define QV4ERROROBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct SyntaxErrorObject; + +struct ErrorObject: Object { + enum ErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError + }; + + ErrorObject(ExecutionContext *context, const Value &message, ErrorType t = Error); + + SyntaxErrorObject *asSyntaxError(); +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(ExecutionContext *ctx, const Value &message); +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(ExecutionContext *ctx, const Value &message); + RangeErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(ExecutionContext *ctx, const Value &message); + ReferenceErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } + static void destroy(Managed *that) { static_cast<SyntaxErrorObject *>(that)->~SyntaxErrorObject(); } + + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(ExecutionContext *ctx, const Value &message); + TypeErrorObject(ExecutionContext *ctx, const QString &msg); +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(ExecutionContext *ctx, const Value &message); +}; + +struct ErrorCtor: FunctionObject +{ + ErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(ExecutionContext *scope): ErrorCtor(scope) { vtbl = &static_vtbl; } + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(ExecutionContext *context): ErrorObject(context, Value::undefinedValue()) {} + void init(ExecutionContext *ctx, const Value &ctor) { init(ctx, ctor, this); } + + static void init(ExecutionContext *ctx, const Value &ctor, Object *obj); + static Value method_toString(SimpleCallContext *ctx); +}; + +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(ExecutionContext *ctx): EvalErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(ExecutionContext *ctx): RangeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(ExecutionContext *ctx): ReferenceErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(ExecutionContext *ctx): SyntaxErrorObject(ctx, 0) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(ExecutionContext *ctx): TypeErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(ExecutionContext *ctx): URIErrorObject(ctx, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionContext *ctx, const Value &ctor) { ErrorPrototype::init(ctx, ctor, this); } +}; + + +inline SyntaxErrorObject *ErrorObject::asSyntaxError() +{ + return subtype == SyntaxError ? static_cast<SyntaxErrorObject *>(this) : 0; +} + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4executableallocator.cpp b/src/qml/qml/v4vm/qv4executableallocator.cpp new file mode 100644 index 0000000000..2ad300a74b --- /dev/null +++ b/src/qml/qml/v4vm/qv4executableallocator.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4executableallocator.h" + +#include <assert.h> +#include <wtf/StdLibExtras.h> +#include <wtf/PageAllocation.h> + +using namespace QQmlJS::VM; + +void *ExecutableAllocator::Allocation::start() const +{ + return reinterpret_cast<void*>(addr); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) +{ + Allocation *remainder = new Allocation; + if (next) + next->prev = remainder; + + remainder->next = next; + next = remainder; + + remainder->prev = this; + + remainder->size = size - dividingSize; + remainder->free = free; + remainder->addr = addr + dividingSize; + size = dividingSize; + + return remainder; +} + +bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator) +{ + assert(free); + if (!next || !next->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(next->size, next); + + size += next->size; + Allocation *newNext = next->next; + delete next; + next = newNext; + if (next) + next->prev = this; + + allocator->freeAllocations.insert(size, this); + return true; +} + +bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator) +{ + assert(free); + if (!prev || !prev->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(prev->size, prev); + + prev->size += size; + if (next) + next->prev = prev; + prev->next = next; + + allocator->freeAllocations.insert(prev->size, prev); + + delete this; + return true; +} + +ExecutableAllocator::ChunkOfPages::~ChunkOfPages() +{ + Allocation *alloc = firstAllocation; + while (alloc) { + Allocation *next = alloc->next; + delete alloc; + alloc = next; + } + pages->deallocate(); + delete pages; +} + +bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const +{ + Allocation *it = firstAllocation; + while (it) { + if (it == alloc) + return true; + it = it->next; + } + return false; +} + +ExecutableAllocator::~ExecutableAllocator() +{ + qDeleteAll(chunks); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) +{ + Allocation *allocation = 0; + + // Code is best aligned to 16-byte boundaries. + size = WTF::roundUpToMultipleOf(16, size); + + QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size); + if (it != freeAllocations.end()) { + allocation = *it; + freeAllocations.erase(it); + } + + if (!allocation) { + ChunkOfPages *chunk = new ChunkOfPages; + size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size); + chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages)); + chunks.insert(reinterpret_cast<quintptr>(chunk->pages->base()) - 1, chunk); + allocation = new Allocation; + allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base()); + allocation->size = allocSize; + allocation->free = true; + chunk->firstAllocation = allocation; + } + + assert(allocation); + assert(allocation->free); + + allocation->free = false; + + if (allocation->size > size) { + Allocation *remainder = allocation->split(size); + remainder->free = true; + if (!remainder->mergeNext(this)) + freeAllocations.insert(remainder->size, remainder); + } + + return allocation; +} + +void ExecutableAllocator::free(Allocation *allocation) +{ + assert(allocation); + + allocation->free = true; + + QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + assert(it != chunks.end()); + ChunkOfPages *chunk = *it; + assert(chunk->contains(allocation)); + + bool merged = allocation->mergeNext(this); + merged |= allocation->mergePrevious(this); + if (!merged) + freeAllocations.insert(allocation->size, allocation); + + allocation = 0; + + if (!chunk->firstAllocation->next) { + freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation); + chunks.erase(it); + delete chunk; + return; + } +} diff --git a/src/qml/qml/v4vm/qv4executableallocator.h b/src/qml/qml/v4vm/qv4executableallocator.h new file mode 100644 index 0000000000..8e45288c54 --- /dev/null +++ b/src/qml/qml/v4vm/qv4executableallocator.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4EXECUTABLEALLOCATOR_H +#define QV4EXECUTABLEALLOCATOR_H + +#include "qv4global.h" + +#include <QMultiMap> +#include <QHash> +#include <QVector> + +namespace WTF { +struct PageAllocation; +}; + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +class Q_AUTOTEST_EXPORT ExecutableAllocator +{ + struct ChunkOfPages; +public: + struct Allocation; + + ~ExecutableAllocator(); + + Allocation *allocate(size_t size); + void free(Allocation *allocation); + + struct Allocation + { + Allocation() + : addr(0) + , size(0) + , free(true) + , next(0) + , prev(0) + {} + + void *start() const; + + private: + friend class ExecutableAllocator; + + Allocation *split(size_t dividingSize); + bool mergeNext(ExecutableAllocator *allocator); + bool mergePrevious(ExecutableAllocator *allocator); + + quintptr addr; + uint size : 31; // More than 2GB of function code? nah :) + uint free : 1; + Allocation *next; + Allocation *prev; + }; + + // for debugging / unit-testing + int freeAllocationCount() const { return freeAllocations.count(); } + int chunkCount() const { return chunks.count(); } + +private: + struct ChunkOfPages + { + ~ChunkOfPages(); + + WTF::PageAllocation *pages; + Allocation *firstAllocation; + + bool contains(Allocation *alloc) const; + }; + + QMultiMap<size_t, Allocation*> freeAllocations; + QMap<quintptr, ChunkOfPages*> chunks; +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4EXECUTABLEALLOCATOR_H diff --git a/src/qml/qml/v4vm/qv4functionobject.cpp b/src/qml/qml/v4vm/qv4functionobject.cpp new file mode 100644 index 0000000000..2380c1a994 --- /dev/null +++ b/src/qml/qml/v4vm/qv4functionobject.cpp @@ -0,0 +1,522 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include "qv4alloca_p.h" + +using namespace QQmlJS::VM; + + +DEFINE_MANAGED_VTABLE(FunctionObject); + +Function::~Function() +{ + delete[] codeData; +} + +void Function::mark() +{ + if (name) + name->mark(); + for (int i = 0; i < formals.size(); ++i) + formals.at(i)->mark(); + for (int i = 0; i < locals.size(); ++i) + locals.at(i)->mark(); + for (int i = 0; i < generatedValues.size(); ++i) + if (Managed *m = generatedValues.at(i).asManaged()) + m->mark(); + for (int i = 0; i < identifiers.size(); ++i) + identifiers.at(i)->mark(); +} + +FunctionObject::FunctionObject(ExecutionContext *scope) + : Object(scope->engine) + , scope(scope) + , name(0) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) + , function(0) +{ + vtbl = &static_vtbl; + prototype = scope->engine->functionPrototype; + + type = Type_FunctionObject; + needsActivation = true; + usesArgumentsObject = false; + strictMode = false; +#ifndef QT_NO_DEBUG + assert(scope->next != (ExecutionContext *)0x1); +#endif +} + +bool FunctionObject::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value) +{ + FunctionObject *f = static_cast<FunctionObject *>(that); + + Object *v = value.asObject(); + if (!v) + return false; + + Object *o = f->get(ctx, ctx->engine->id_prototype).asObject(); + if (!o) + ctx->throwTypeError(); + + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o == v) + return true; + } + + return false; +} + +Value FunctionObject::construct(Managed *that, ExecutionContext *context, Value *, int) +{ + FunctionObject *f = static_cast<FunctionObject *>(that); + + Object *obj = context->engine->newObject(); + Value proto = f->get(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); +} + +Value FunctionObject::call(Managed *, ExecutionContext *, const Value &, Value *, int) +{ + return Value::undefinedValue(); +} + +void FunctionObject::markObjects(Managed *that) +{ + FunctionObject *o = static_cast<FunctionObject *>(that); + if (o->name) + o->name->mark(); + // these are marked in VM::Function: +// for (uint i = 0; i < formalParameterCount; ++i) +// formalParameterList[i]->mark(); +// for (uint i = 0; i < varCount; ++i) +// varList[i]->mark(); + o->scope->mark(); + if (o->function) + o->function->mark(); + + Object::markObjects(that); +} + + +DEFINE_MANAGED_VTABLE(FunctionCtor); + +FunctionCtor::FunctionCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +// 15.3.2 +Value FunctionCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc) +{ + FunctionCtor *f = static_cast<FunctionCtor *>(that); + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + QString arguments; + QString body; + if (argc > 0) { + for (uint i = 0; i < argc - 1; ++i) { + if (i) + arguments += QLatin1String(", "); + arguments += args[i].toString(ctx)->toQString(); + } + body = args[argc - 1].toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(function, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + ctx->throwSyntaxError(0); + + using namespace AST; + FunctionExpression *fe = AST::cast<FunctionExpression *>(parser.rootNode()); + if (!fe) + ctx->throwSyntaxError(0); + + V4IR::Module module; + + Codegen cg(ctx, f->strictMode); + V4IR::Function *irf = cg(QString(), function, fe, &module); + + QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(ctx->engine, &module)); + VM::Function *vmf = isel->vmFunction(irf); + + return Value::fromObject(ctx->engine->newScriptFunction(ctx->engine->rootContext, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +Value FunctionCtor::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) +{ + return construct(that, context, args, argc); +} + +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); + +} + +Value FunctionPrototype::method_toString(SimpleCallContext *ctx) +{ + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) + ctx->throwTypeError(); + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); +} + +Value FunctionPrototype::method_apply(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + Value arg = ctx->argument(1); + QVector<Value> args; + + if (Object *arr = arg.asObject()) { + quint32 len = arr->get(ctx, ctx->engine->id_length).toUInt32(); + for (quint32 i = 0; i < len; ++i) { + Value a = arr->getIndexed(ctx, i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_call(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + QVector<Value> args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(ctx, thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_bind(SimpleCallContext *ctx) +{ + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) + ctx->throwTypeError(); + + Value boundThis = ctx->argument(0); + QVector<Value> boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx->engine->rootContext, target, boundThis, boundArgs); + return Value::fromObject(f); +} + + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ScriptFunction); + +ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; + this->function = function; + assert(function); + assert(function->code); + + // global function + if (!scope) + return; + + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + + name = function->name; + needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; + strictMode = function->isStrict; + formalParameterCount = function->formals.size(); + formalParameterList = function->formals.constData(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); + + varCount = function->locals.size(); + varList = function->locals.constData(); + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); + pd->value = Value::fromObject(proto); + + if (scope->strictMode) { + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + } +} + +Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value *args, int argc) +{ + ScriptFunction *f = static_cast<ScriptFunction *>(that); + assert(f->function->code); + Object *obj = context->engine->newObject(); + Value proto = f->get(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *ctx = context->engine->newCallContext(stackSpace, f, Value::fromObject(obj), args, argc); + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + +Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) +{ + ScriptFunction *f = static_cast<ScriptFunction *>(that); + assert(f->function->code); + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *ctx = context->engine->newCallContext(stackSpace, f, thisObject, args, argc); + + if (!f->strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) { + ctx->thisObject = Value::fromObject(context->engine->globalObject); + } else { + ctx->thisObject = Value::fromObject(thisObject.toObject(context)); + } + } + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + return result; +} + + + +DEFINE_MANAGED_VTABLE(BuiltinFunctionOld); + +BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) + : FunctionObject(scope) + , code(code) +{ + vtbl = &static_vtbl; + this->name = name; + isBuiltinFunction = true; +} + +Value BuiltinFunctionOld::construct(Managed *, ExecutionContext *ctx, Value *, int) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) +{ + BuiltinFunctionOld *f = static_cast<BuiltinFunctionOld *>(that); + SimpleCallContext ctx; + ctx.type = ExecutionContext::Type_SimpleCallContext; + ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? + ctx.marked = false; + ctx.thisObject = thisObject; + ctx.engine = f->scope->engine; + ctx.arguments = args; + ctx.argumentCount = argc; + context->engine->pushContext(&ctx); + + if (!f->strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (!thisObject.isUndefined() && !thisObject.isNull()) + ctx.thisObject = Value::fromObject(thisObject.toObject(context)); + } + + Value result = Value::undefinedValue(); + try { + result = f->code(&ctx); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + + context->engine->popContext(); + return result; +} + + +DEFINE_MANAGED_VTABLE(BoundFunction); + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs) + : FunctionObject(scope) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + vtbl = &static_vtbl; + int len = target->get(scope, scope->engine->id_length).toUInt32(); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; +} + +void BoundFunction::destroy(Managed *that) +{ + static_cast<BoundFunction *>(that)->~BoundFunction(); +} + +Value BoundFunction::call(Managed *that, ExecutionContext *context, const Value &, Value *args, int argc) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->call(context, f->boundThis, newArgs, f->boundArgs.size() + argc); +} + +Value BoundFunction::construct(Managed *that, ExecutionContext *context, Value *args, int argc) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->construct(context, newArgs, f->boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(Managed *that, ExecutionContext *ctx, const Value &value) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + return FunctionObject::hasInstance(f->target, ctx, value); +} + +void BoundFunction::markObjects(Managed *that) +{ + BoundFunction *o = static_cast<BoundFunction *>(that); + o->target->mark(); + if (Managed *m = o->boundThis.asManaged()) + m->mark(); + for (int i = 0; i < o->boundArgs.size(); ++i) + if (Managed *m = o->boundArgs.at(i).asManaged()) + m->mark(); + FunctionObject::markObjects(that); +} diff --git a/src/qml/qml/v4vm/qv4functionobject.h b/src/qml/qml/v4vm/qv4functionobject.h new file mode 100644 index 0000000000..4dddc048f9 --- /dev/null +++ b/src/qml/qml/v4vm/qv4functionobject.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTIONOBJECT_H +#define QV4FUNCTIONOBJECT_H + +#include "qv4global.h" +#include "qv4runtime.h" +#include "qv4engine.h" +#include "qv4context.h" +#include "qv4object.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4property.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +#include <config.h> +#include <assembler/MacroAssemblerCodeRef.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Function; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct InternalClass; +struct Lookup; + +struct Function { + String *name; + + VM::Value (*code)(VM::ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + quint32 codeSize; + QByteArray unwindInfo; // CIE+FDE on x86/x86-64. Stored directly in code on ARM. + + QVector<String *> formals; + QVector<String *> locals; + QVector<Value> generatedValues; + QVector<String *> identifiers; + QVector<Function *> nestedFunctions; + Function *outer; + + Lookup *lookups; + + bool hasNestedFunctions; + bool hasDirectEval; + bool usesArgumentsObject; + bool isStrict; + bool isNamedExpression; + + Function(String *name) + : name(name) + , code(0) + , codeData(0) + , codeSize(0) + , outer(0) + , lookups(0) + , hasNestedFunctions(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + {} + ~Function(); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } + + void mark(); +}; + +struct Q_V4_EXPORT FunctionObject: Object { + ExecutionContext *scope; + String *name; + String * const *formalParameterList; + String * const *varList; + unsigned int formalParameterCount; + unsigned int varCount; + VM::Function *function; + + FunctionObject(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + inline Value construct(ExecutionContext *context, Value *args, int argc) { + return vtbl->construct(this, context, args, argc); + } + inline Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, context, thisObject, args, argc); + } + +protected: + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); +}; + +struct FunctionCtor: FunctionObject +{ + FunctionCtor(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(ExecutionContext *ctx): FunctionObject(ctx) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_apply(SimpleCallContext *ctx); + static Value method_call(SimpleCallContext *ctx); + static Value method_bind(SimpleCallContext *ctx); +}; + +struct BuiltinFunctionOld: FunctionObject { + Value (*code)(SimpleCallContext *); + + BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ScriptFunction: FunctionObject { + ScriptFunction(ExecutionContext *scope, VM::Function *function); + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector<Value> boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs); + ~BoundFunction() {} + + + static Value construct(Managed *, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + + static const ManagedVTable static_vtbl; + static void destroy(Managed *); + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4vm/qv4global.h b/src/qml/qml/v4vm/qv4global.h new file mode 100644 index 0000000000..5f3f1b6d17 --- /dev/null +++ b/src/qml/qml/v4vm/qv4global.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GLOBAL_H +#define QV4GLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_V4_LIB) +# define Q_V4_EXPORT Q_DECL_EXPORT +# else +# define Q_V4_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_V4_EXPORT +#endif + +namespace QQmlJS { +namespace VM { + + +enum PropertyFlag { + Attr_Data = 0, + Attr_Accessor = 0x1, + Attr_NotWritable = 0x2, + Attr_NotEnumerable = 0x4, + Attr_NotConfigurable = 0x8, + Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_Invalid = 0xff +}; + +Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag); +Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags); + +struct PropertyAttributes +{ + union { + uchar m_all; + struct { + uchar m_flags : 4; + uchar m_mask : 4; + }; + struct { + uchar m_type : 1; + uchar m_writable : 1; + uchar m_enumerable : 1; + uchar m_configurable : 1; + uchar type_set : 1; + uchar writable_set : 1; + uchar enumerable_set : 1; + uchar configurable_set : 1; + }; + }; + + enum Type { + Data = 0, + Accessor = 1, + Generic = 2 + }; + + PropertyAttributes() : m_all(0) {} + PropertyAttributes(PropertyFlag f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(PropertyFlags f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} + PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } + + void setType(Type t) { m_type = t; type_set = true; } + Type type() const { return type_set ? (Type)m_type : Generic; } + + bool isData() const { return type() == PropertyAttributes::Data || writable_set; } + bool isAccessor() const { return type() == PropertyAttributes::Accessor; } + bool isGeneric() const { return type() == PropertyAttributes::Generic && !writable_set; } + + bool hasType() const { return type_set; } + bool hasWritable() const { return writable_set; } + bool hasConfigurable() const { return configurable_set; } + bool hasEnumerable() const { return enumerable_set; } + + void setWritable(bool b) { m_writable = b; writable_set = true; } + void setConfigurable(bool b) { m_configurable = b; configurable_set = true; } + void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; } + + void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } } + + bool isWritable() const { return m_type != Data || m_writable; } + bool isEnumerable() const { return m_enumerable; } + bool isConfigurable() const { return m_configurable; } + + void clearType() { m_type = Data; type_set = false; } + void clearWritable() { m_writable = false; writable_set = false; } + void clearEnumerable() { m_enumerable = false; enumerable_set = false; } + void clearConfigurable() { m_configurable = false; configurable_set = false; } + + void clear() { m_all = 0; } + bool isEmpty() const { return !m_all; } + + uint flags() const { return m_flags; } + + bool operator==(PropertyAttributes other) { + return m_all == other.m_all; + } + bool operator!=(PropertyAttributes other) { + return m_all != other.m_all; + } +}; + +} +} + +QT_END_NAMESPACE + +#endif // QV4GLOBAL_H diff --git a/src/qml/qml/v4vm/qv4globalobject.cpp b/src/qml/qml/v4vm/qv4globalobject.cpp new file mode 100644 index 0000000000..75da51a9b6 --- /dev/null +++ b/src/qml/qml/v4vm/qv4globalobject.cpp @@ -0,0 +1,731 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4globalobject.h" +#include "qv4mm.h" +#include "qv4value.h" +#include "qv4context.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/QDebug> +#include <QtCore/QString> +#include <iostream> +#include "qv4alloca_p.h" + +#include <wtf/MathExtras.h> + +using namespace QQmlJS::VM; + +static inline char toHex(char c) +{ + static const char hexnumbers[] = "0123456789ABCDEF"; + return hexnumbers[c & 0xf]; +} + +static int fromHex(QChar ch) +{ + ushort c = ch.unicode(); + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + return -1; +} + +static QString escape(const QString &input) +{ + QString output; + output.reserve(input.size() * 3); + const int length = input.length(); + for (int i = 0; i < length; ++i) { + ushort uc = input.at(i).unicode(); + if (uc < 0x100) { + if ( (uc > 0x60 && uc < 0x7B) + || (uc > 0x3F && uc < 0x5B) + || (uc > 0x2C && uc < 0x3A) + || (uc == 0x2A) + || (uc == 0x2B) + || (uc == 0x5F)) { + output.append(QChar(uc)); + } else { + output.append('%'); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } else { + output.append('%'); + output.append('u'); + output.append(QChar(toHex(uc >> 12))); + output.append(QChar(toHex(uc >> 8))); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } + return output; +} + +static QString unescape(const QString &input) +{ + QString result; + result.reserve(input.length()); + int i = 0; + const int length = input.length(); + while (i < length) { + QChar c = input.at(i++); + if ((c == '%') && (i + 1 < length)) { + QChar a = input.at(i); + if ((a == 'u') && (i + 4 < length)) { + int d3 = fromHex(input.at(i+1)); + int d2 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+3)); + int d0 = fromHex(input.at(i+4)); + if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { + ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); + result.append(QChar(uc)); + i += 5; + } else { + result.append(c); + } + } else { + int d1 = fromHex(a); + int d0 = fromHex(input.at(i+1)); + if ((d1 != -1) && (d0 != -1)) { + c = (d1 << 4) | d0; + i += 2; + } + result.append(c); + } + } else { + result.append(c); + } + } + return result; +} + +static const char uriReserved[] = ";/?:@&=+$,#"; +static const char uriUnescaped[] = "-_.!~*'()"; +static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; + +static void addEscapeSequence(QString &output, uchar ch) +{ + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(ch >> 4))); + output.append(QLatin1Char(toHex(ch & 0xf))); +} + +static QString encode(const QString &input, const char *unescapedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + while (i < length) { + const QChar c = input.at(i); + bool escape = true; + if ((c.unicode() >= 'a' && c.unicode() <= 'z') || + (c.unicode() >= 'A' && c.unicode() <= 'Z') || + (c.unicode() >= '0' && c.unicode() <= '9')) { + escape = false; + } else { + const char *r = unescapedSet; + while (*r) { + if (*r == c.unicode()) { + escape = false; + break; + } + ++r; + } + } + if (escape) { + uint uc = c.unicode(); + if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { + *ok = false; + break; + } + if (!((uc < 0xD800) || (uc > 0xDBFF))) { + ++i; + if (i == length) { + *ok = false; + break; + } + const uint uc2 = input.at(i).unicode(); + if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { + *ok = false; + break; + } + uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; + } + if (uc < 0x80) { + addEscapeSequence(output, (uchar)uc); + } else { + if (uc < 0x0800) { + addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); + } else { + + if (QChar::requiresSurrogates(uc)) { + addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); + } else { + addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); + } + } else { + output.append(c); + } + ++i; + } + if (i != length) + *ok = false; + return output; +} + +enum DecodeMode { + DecodeAll, + DecodeNonReserved +}; + +static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) +{ + *ok = true; + QString output; + output.reserve(input.length()); + const int length = input.length(); + int i = 0; + const QChar percent = QLatin1Char('%'); + while (i < length) { + const QChar ch = input.at(i); + if (ch == percent) { + int start = i; + if (i + 2 >= length) + goto error; + + int d1 = fromHex(input.at(i+1)); + int d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + int b = (d1 << 4) | d0; + i += 2; + if (b & 0x80) { + int uc; + int min_uc; + int need; + if ((b & 0xe0) == 0xc0) { + uc = b & 0x1f; + need = 1; + min_uc = 0x80; + } else if ((b & 0xf0) == 0xe0) { + uc = b & 0x0f; + need = 2; + min_uc = 0x800; + } else if ((b & 0xf8) == 0xf0) { + uc = b & 0x07; + need = 3; + min_uc = 0x10000; + } else { + goto error; + } + + if (i + (3 * need) >= length) + goto error; + + for (int j = 0; j < need; ++j) { + ++i; + if (input.at(i) != percent) + goto error; + + d1 = fromHex(input.at(i+1)); + d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + b = (d1 << 4) | d0; + if ((b & 0xC0) != 0x80) + goto error; + + i += 2; + uc = (uc << 6) + (b & 0x3f); + } + if (uc < min_uc) + goto error; + + if (uc < 0x10000) { + output.append(QChar(uc)); + } else { + if (uc > 0x10FFFF) + goto error; + + ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); + ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); + output.append(QChar(h)); + output.append(QChar(l)); + } + } else { + if (decodeMode == DecodeNonReserved && b <= 0x40) { + const char *r = uriReserved; + while (*r) { + if (*r == b) + break; + ++r; + } + if (*r) + output.append(input.mid(start, i - start + 1)); + else + output.append(QChar(b)); + } else { + output.append(QChar(b)); + } + } + } else { + output.append(ch); + } + ++i; + } + if (i != length) + *ok = false; + return output; + error: + *ok = false; + return QString(); +} + +DEFINE_MANAGED_VTABLE(EvalFunction); + +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope) + , qmlActivation(0) +{ + vtbl = &static_vtbl; + name = scope->engine->id_eval; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); +} + +EvalFunction::EvalFunction(ExecutionContext *scope, Object *qmlActivation) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; + name = scope->engine->id_eval; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + this->qmlActivation = qmlActivation; +} + +Value EvalFunction::evalCall(ExecutionContext *parentContext, Value /*thisObject*/, Value *args, int argc, bool directCall) +{ + if (argc < 1) + return Value::undefinedValue(); + + ExecutionEngine *engine = parentContext->engine; + ExecutionContext *ctx = parentContext; + + if (!directCall) { + // the context for eval should be the global scope, so we fake a root + // context + ctx = engine->pushGlobalContext(); + } + + if (!args[0].isString()) + return args[0]; + + const QString code = args[0].stringValue()->toQString(); + bool inheritContext = !ctx->strictMode; + + QQmlJS::VM::Function *f = parseSource(ctx, QStringLiteral("eval code"), + code, QQmlJS::Codegen::EvalCode, + (directCall && parentContext->strictMode), inheritContext); + + if (!f) + return Value::undefinedValue(); + + strictMode = f->isStrict || (ctx->strictMode); + if (qmlActivation) + strictMode = true; + + usesArgumentsObject = f->usesArgumentsObject; + needsActivation = f->needsActivation(); + + if (strictMode) { + CallContext *k = ctx->engine->newCallContext(this, ctx->thisObject, 0, 0); + if (qmlActivation) { + k->activation = qmlActivation; + k->type = ExecutionContext::Type_QmlContext; + } + ctx = k; + } + + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strictMode; + + Value result = Value::undefinedValue(); + try { + result = f->code(ctx, f->codeData); + } catch (Exception &ex) { + ctx->strictMode = cstrict; + if (strictMode) + ex.partiallyUnwindContext(parentContext); + throw; + } + + ctx->strictMode = cstrict; + + while (engine->current != parentContext) + engine->popContext(); + + return result; +} + + +Value EvalFunction::call(Managed *that, ExecutionContext *context, const Value &thisObject, Value *args, int argc) +{ + // indirect call + return static_cast<EvalFunction *>(that)->evalCall(context, thisObject, args, argc, false); +} + +//Value EvalFunction::construct(ExecutionContext *ctx, Value *, int) +//{ +// ctx->throwTypeError(); +// return Value::undefinedValue(); +//} + +QQmlJS::VM::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, const QString &source, + QQmlJS::Codegen::Mode mode, + bool strictMode, bool inheritContext) +{ + using namespace QQmlJS; + + MemoryManager::GCBlocker gcBlocker(ctx->engine->memoryManager); + + VM::ExecutionEngine *vm = ctx->engine; + V4IR::Module module; + VM::Function *globalCode = 0; + + { + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(source, 1, false); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + VM::DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new VM::DiagnosticMessage; + (*errIt)->fileName = fileName; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = VM::DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + std::cerr << qPrintable(fileName) << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << qPrintable(m.message) << std::endl; + } + } + if (error) + ctx->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast<Program *>(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return 0; + } + + QStringList inheritedLocals; + if (inheritContext) + for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + + Codegen cg(ctx, strictMode); + V4IR::Function *globalIRCode = cg(fileName, source, program, &module, mode, inheritedLocals); + QScopedPointer<EvalInstructionSelection> isel(ctx->engine->iselFactory->create(vm, &module)); + if (inheritContext) + isel->setUseFastLookups(false); + if (globalIRCode) + globalCode = isel->vmFunction(globalIRCode); + } + + if (! globalCode) + // ### should be a syntax error + ctx->throwTypeError(); + } + + return globalCode; +} + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +// parseInt [15.1.2.2] +Value GlobalFunctions::method_parseInt(SimpleCallContext *context) +{ + Value string = context->argument(0); + Value radix = context->argument(1); + int R = radix.isUndefined() ? 0 : radix.toInt32(); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } + } + + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } +} + +// parseFloat [15.1.2.3] +Value GlobalFunctions::method_parseFloat(SimpleCallContext *context) +{ + Value string = context->argument(0); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(Q_INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-Q_INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 3 + else + return Value::fromDouble(d); +} + +/// isNaN [15.1.2.4] +Value GlobalFunctions::method_isNaN(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(); + return Value::fromBoolean(isnan(d)); +} + +/// isFinite [15.1.2.5] +Value GlobalFunctions::method_isFinite(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(); + return Value::fromBoolean(std::isfinite(d)); +} + +/// decodeURI [15.1.3.1] +Value GlobalFunctions::method_decodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeNonReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// decodeURIComponent [15.1.3.2] +Value GlobalFunctions::method_decodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeAll, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURI [15.1.3.3] +Value GlobalFunctions::method_encodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescapedReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURIComponent [15.1.3.4] +Value GlobalFunctions::method_encodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescaped, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +Value GlobalFunctions::method_escape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, escape(str)); +} + +Value GlobalFunctions::method_unescape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, unescape(str)); +} diff --git a/src/qml/qml/v4vm/qv4globalobject.h b/src/qml/qml/v4vm/qv4globalobject.h new file mode 100644 index 0000000000..f9776f95cb --- /dev/null +++ b/src/qml/qml/v4vm/qv4globalobject.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4GLOBALOBJECT_H +#define QV4GLOBALOBJECT_H + +#include "qv4global.h" +#include "qv4functionobject.h" + +QT_END_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Q_V4_EXPORT EvalFunction : FunctionObject +{ + EvalFunction(ExecutionContext *scope); + EvalFunction(ExecutionContext *scope, Object *qmlActivation); + + static QQmlJS::VM::Function *parseSource(QQmlJS::VM::ExecutionContext *ctx, + const QString &fileName, + const QString &source, + QQmlJS::Codegen::Mode mode, bool strictMode, + bool inheritContext); + + Value evalCall(ExecutionContext *context, Value thisObject, Value *args, int argc, bool directCall); + + using Managed::construct; + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + + Object *qmlActivation; + +protected: + static const ManagedVTable static_vtbl; +}; + +struct GlobalFunctions +{ + static Value method_parseInt(SimpleCallContext *context); + static Value method_parseFloat(SimpleCallContext *context); + static Value method_isNaN(SimpleCallContext *context); + static Value method_isFinite(SimpleCallContext *context); + static Value method_decodeURI(SimpleCallContext *context); + static Value method_decodeURIComponent(SimpleCallContext *context); + static Value method_encodeURI(SimpleCallContext *context); + static Value method_encodeURIComponent(SimpleCallContext *context); + static Value method_escape(SimpleCallContext *context); + static Value method_unescape(SimpleCallContext *context); +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4vm/qv4identifier.h b/src/qml/qml/v4vm/qv4identifier.h new file mode 100644 index 0000000000..1467e80986 --- /dev/null +++ b/src/qml/qml/v4vm/qv4identifier.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIER_H +#define QV4IDENTIFIER_H + +#include <qv4string.h> +#include <qv4engine.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Identifiers +{ + ExecutionEngine *engine; + uint currentIndex; + QHash<QString, String *> identifiers; +public: + + Identifiers(ExecutionEngine *engine) : engine(engine), currentIndex(0) {} + + String *insert(const QString &s) + { + QHash<QString, String*>::const_iterator it = identifiers.find(s); + if (it != identifiers.constEnd()) + return it.value(); + + String *str = engine->newString(s); + str->createHashValue(); + if (str->subtype == String::StringType_ArrayIndex) + return str; + + str->identifier = currentIndex; + if (currentIndex <= USHRT_MAX) { + identifiers.insert(s, str); + ++currentIndex; + } + return str; + } + + void toIdentifier(String *s) { + if (s->identifier != UINT_MAX) + return; + if (s->subtype == String::StringType_Unknown) + s->createHashValue(); + if (s->subtype == String::StringType_ArrayIndex) + return; + QHash<QString, String*>::const_iterator it = identifiers.find(s->toQString()); + if (it != identifiers.constEnd()) { + s->identifier = (*it)->identifier; + return; + } + s->identifier = currentIndex; + if (currentIndex <= USHRT_MAX) { + identifiers.insert(s->toQString(), s); + ++currentIndex; + } + } + + void mark() { + for (QHash<QString, String *>::const_iterator it = identifiers.constBegin(); it != identifiers.constEnd(); ++it) + (*it)->mark(); + } +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4internalclass.cpp b/src/qml/qml/v4vm/qv4internalclass.cpp new file mode 100644 index 0000000000..4d9bb93d0d --- /dev/null +++ b/src/qml/qml/v4vm/qv4internalclass.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4internalclass.h> +#include <qv4string.h> +#include <qv4engine.h> +#include <qv4identifier.h> + +namespace QQmlJS { +namespace VM { + + +InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other) + : engine(other.engine) + , propertyTable(other.propertyTable) + , nameMap(other.nameMap) + , propertyData(other.propertyData) + , transitions() + , m_sealed(0) + , m_frozen(0) + , size(other.size) +{ +} + +// ### Should we build this up from the empty class to avoid duplication? +InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all; + data.resolve(); + uint idx = find(string); + if (index) + *index = idx; + + assert(idx != UINT_MAX); + + if (data == propertyData[idx]) + return this; + + uint tid = string->identifier | (data.flags() << 27); + + QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(tid); + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyData[idx] = data; + return newClass; + +} + +InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type(); + data.resolve(); + engine->identifierCache->toIdentifier(string); + uint id = string->identifier | (data.flags() << 27); + + assert(propertyTable.constFind(id) == propertyTable.constEnd()); + + QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(id); + + if (index) + *index = size; + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyTable.insert(string->identifier, size); + newClass->nameMap.append(string); + newClass->propertyData.append(data); + ++newClass->size; + transitions.insert(id, newClass); + return newClass; +} + +void InternalClass::removeMember(Object *object, uint id) +{ + assert (propertyTable.constFind(id) != propertyTable.constEnd()); + int propIdx = propertyTable.constFind(id).value(); + assert(propIdx < size); + + int toRemove = - (int)id; + QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(toRemove); + + if (tit != transitions.constEnd()) { + object->internalClass = tit.value(); + return; + } + + // create a new class and add it to the tree + object->internalClass = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + if (i == propIdx) + continue; + object->internalClass = object->internalClass->addMember(nameMap.at(i), propertyData.at(i)); + } + + transitions.insert(toRemove, object->internalClass); +} + +uint InternalClass::find(String *string) +{ + engine->identifierCache->toIdentifier(string); + uint id = string->identifier; + + QHash<uint, uint>::const_iterator it = propertyTable.constFind(id); + if (it != propertyTable.constEnd()) + return it.value(); + + return UINT_MAX; +} + +InternalClass *InternalClass::sealed() +{ + if (m_sealed) + return m_sealed; + + m_sealed = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setConfigurable(false); + m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + } + + m_sealed->m_sealed = m_sealed; + return m_sealed; +} + +InternalClass *InternalClass::frozen() +{ + if (m_frozen) + return m_frozen; + + m_frozen = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setWritable(false); + attrs.setConfigurable(false); + m_frozen = m_frozen->addMember(nameMap.at(i), attrs); + } + + m_frozen->m_frozen = m_frozen; + return m_frozen; +} + + +} +} diff --git a/src/qml/qml/v4vm/qv4internalclass.h b/src/qml/qml/v4vm/qv4internalclass.h new file mode 100644 index 0000000000..cc3b03190b --- /dev/null +++ b/src/qml/qml/v4vm/qv4internalclass.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4INTERNALCLASS_H +#define QV4INTERNALCLASS_H + +#include <QHash> +#include <QVector> +#include <qv4global.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct String; +struct ExecutionEngine; +struct Object; + +struct InternalClass { + ExecutionEngine *engine; + QHash<uint, uint> propertyTable; // id to valueIndex + QVector<String *> nameMap; + + QVector<PropertyAttributes> propertyData; + + QHash<int, InternalClass *> transitions; // id to next class, positive means add, negative delete + + InternalClass *m_sealed; + InternalClass *m_frozen; + + uint size; + + InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {} + + InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); + void removeMember(Object *object, uint id); + uint find(String *s); + + InternalClass *sealed(); + InternalClass *frozen(); + +private: + InternalClass(const InternalClass &other); +}; + + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4isel_llvm.cpp b/src/qml/qml/v4vm/qv4isel_llvm.cpp new file mode 100644 index 0000000000..27614bae62 --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_llvm.cpp @@ -0,0 +1,1375 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include <llvm/Analysis/Passes.h> +#include <llvm/Analysis/Verifier.h> +#include <llvm/Assembly/PrintModulePass.h> +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/ExecutionEngine/ExecutionEngine.h> +#include <llvm/ExecutionEngine/JIT.h> +#include <llvm/ExecutionEngine/JITMemoryManager.h> +#include <llvm/Support/FormattedStream.h> +#include <llvm/Support/Host.h> +#include <llvm/Support/MemoryBuffer.h> +#include <llvm/Support/raw_ostream.h> +#include <llvm/Support/system_error.h> +#include <llvm/Support/TargetRegistry.h> +#include <llvm/Support/TargetSelect.h> +#include <llvm/Target/TargetMachine.h> +#include <llvm/Transforms/Scalar.h> +#include <llvm/Transforms/IPO.h> +#include <llvm/Linker.h> + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include <QtCore/QFileInfo> +#include <QtCore/QLibrary> +#include <QtCore/QStringList> +#include <QtCore/QTextStream> +#include <cstdio> +#include <iostream> + +// These includes have to come last, because WTF/Platform.h defines some macros +// with very unfriendly names that collide with class fields in LLVM. +#include "qv4isel_llvm_p.h" +#include "qv4_llvm_p.h" +#include "qv4jsir_p.h" +#include "qv4string.h" +#include "qv4global.h" + +namespace QQmlJS { + +Q_V4_EXPORT int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) +{ + Q_ASSERT(module); + Q_ASSERT(exec || outputType != LLVMOutputJit); + + // TODO: should this be done here? + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + LLVMInitializeX86TargetMC(); + + //---- + + llvm::InitializeNativeTarget(); + LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext()); + + const QString moduleName = QFileInfo(fileName).fileName(); + llvm::StringRef moduleId(moduleName.toUtf8().constData()); + llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); + + if (outputType == LLVMOutputJit) { + // The execution engine takes ownership of the model. No need to delete it anymore. + std::string errStr; + llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) +// .setUseMCJIT(true) + .setErrorStr(&errStr).create(); + if (!execEngine) { + std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; + return EXIT_FAILURE; + } + + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. Start with registering info about how the + // target lays out data structures. + functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + llvm::Function *entryPoint = llvmModule->getFunction("%entry"); + Q_ASSERT(entryPoint); + void *funcPtr = execEngine->getPointerToFunction(entryPoint); + return exec(funcPtr); + } else { + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + // TODO: if output type is .ll, print the module to file + + const std::string triple = llvm::sys::getDefaultTargetTriple(); + + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } + + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::TargetMachine::CodeGenFileType ft; + QString ofName; + + if (outputType == LLVMOutputObject) { + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + } else if (outputType == LLVMOutputAssembler) { + ft = llvm::TargetMachine::CGFT_AssemblyFile; + ofName = fileName + QLatin1String(".s"); + } else { + // ft is not used. + ofName = fileName + QLatin1String(".ll"); + } + + llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); + llvm::formatted_raw_ostream destf(dest); + if (!err.empty()) { + std::cerr << err << std::endl; + delete llvmModule; + } + + llvm::PassManager globalPassManager; + globalPassManager.add(llvm::createScalarReplAggregatesPass()); + globalPassManager.add(llvm::createInstructionCombiningPass()); + globalPassManager.add(llvm::createGlobalOptimizerPass()); + globalPassManager.add(llvm::createFunctionInliningPass(25)); +// globalPassManager.add(llvm::createFunctionInliningPass(125)); + + if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { + if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + globalPassManager.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + } else { // .ll + globalPassManager.run(*llvmModule); + llvmModule->print(destf, 0); + + destf.flush(); + dest.flush(); + } + + delete llvmModule; + return EXIT_SUCCESS; + } +} + +} // QQmlJS + +using namespace QQmlJS; +using namespace QQmlJS::LLVM; + +namespace { +QTextStream qerr(stderr, QIODevice::WriteOnly); +} + +InstructionSelection::InstructionSelection(llvm::LLVMContext &context) + : llvm::IRBuilder<>(context) + , _llvmModule(0) + , _llvmFunction(0) + , _llvmValue(0) + , _numberTy(0) + , _valueTy(0) + , _contextPtrTy(0) + , _stringPtrTy(0) + , _functionTy(0) + , _allocaInsertPoint(0) + , _function(0) + , _block(0) + , _fpm(0) +{ +} + +void InstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) +{ + qSwap(_llvmModule, llvmModule); + qSwap(_fpm, fpm); + + _numberTy = getDoubleTy(); + + std::string err; + + llvm::OwningPtr<llvm::MemoryBuffer> buffer; + qDebug()<<"llvm runtime:"<<LLVM_RUNTIME; + llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef(LLVM_RUNTIME), buffer); + if (ec) { + qWarning() << ec.message().c_str(); + assert(!"cannot load QML/JS LLVM runtime, you can generate the runtime with the command `make llvm_runtime'"); + } + + llvm::Module *llvmRuntime = llvm::getLazyBitcodeModule(buffer.get(), getContext(), &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot load QML/JS LLVM runtime"); + } + + err.clear(); + llvm::Linker::LinkModules(_llvmModule, llvmRuntime, llvm::Linker::DestroySource, &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot link the QML/JS LLVM runtime"); + } + + _valueTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Value"); + _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::ExecutionContext")->getPointerTo(); + _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); + + { + llvm::Type *args[] = { _contextPtrTy }; + _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); + } + + + foreach (IR::Function *function, module->functions) + (void) compileLLVMFunction(function); + qSwap(_fpm, fpm); + qSwap(_llvmModule, llvmModule); +} + +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPopScope() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + llvm::ConstantInt *isDeletable = getInt1(deletable != 0); + llvm::Value *varName = getIdentifier(name); + CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), + _llvmFunction->arg_begin(), isDeletable, varName); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadConst(IR::Const *con, IR::Temp *temp) +{ + llvm::Value *target = getLLVMTemp(temp); + llvm::Value *source = CreateLoad(createValue(con)); + CreateStore(source, target); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setActivationProperty(IR::Temp *source, const QString &targetName) +{ + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, src); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + IR::Function *f = closure->value; + QString name; + if (f->name) + name = *f->name; + + llvm::Value *args[] = { + _llvmFunction->arg_begin(), + getLLVMTemp(target), + getIdentifier(name), + getInt1(f->hasDirectEval), + getInt1(f->usesArgumentsObject), + getInt1(f->isStrict), + getInt1(!f->nestedFunctions.isEmpty()), + genStringList(f->formals, "formals", "formal"), + getInt32(f->formals.size()), + genStringList(f->locals, "locals", "local"), + getInt32(f->locals.size()) + }; + llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure"); + CreateCall(callee, args); +} + +void InstructionSelection::getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target) +{ + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *name = getIdentifier(sourceName); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), t, base, name); +} + +void InstructionSelection::setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"), + _llvmFunction->arg_begin(), base, name, src); +} + +void InstructionSelection::getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); + + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *index = getLLVMTempReference(sourceIndex); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), t, base, index); +} + +void InstructionSelection::setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *index = getLLVMTempReference(targetIndex); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"), + _llvmFunction->arg_begin(), base, index, src); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateStore(s, t); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + const char *opName = 0; + switch (oper) { + case IR::OpNot: opName = "__qmljs_not"; break; + case IR::OpUMinus: opName = "__qmljs_uminus"; break; + case IR::OpUPlus: opName = "__qmljs_uplus"; break; + case IR::OpCompl: opName = "__qmljs_compl"; break; + case IR::OpIncrement: opName = "__qmljs_increment"; break; + case IR::OpDecrement: opName = "__qmljs_decrement"; break; + default: assert(!"unreachable"); break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s); + } +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; + case IR::OpAdd: opName = "__qmljs_llvm_add"; break; + case IR::OpSub: opName = "__qmljs_llvm_sub"; break; + case IR::OpMul: opName = "__qmljs_llvm_mul"; break; + case IR::OpDiv: opName = "__qmljs_llvm_div"; break; + case IR::OpMod: opName = "__qmljs_llvm_mod"; break; + case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; + case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; + case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(target); + llvm::Value *s1 = toValuePtr(leftSource); + llvm::Value *s2 = toValuePtr(rightSource); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s1, s2); + return; + } +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *dst = getIdentifier(targetName); + llvm::Value *src = toValuePtr(rightSource); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), dst, src); + return; + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBaseTemp); + llvm::Value *index = getLLVMTemp(targetIndexTemp); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), base, index, value); + } +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; + case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; + case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; + case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; + case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; + case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; + case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; + case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; + case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; + case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; + case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBase); + llvm::Value *member = getIdentifier(targetName); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), value, base, member); + } +} + +llvm::Function *InstructionSelection::getLLVMFunction(IR::Function *function) +{ + llvm::Function *&f = _functionMap[function]; + if (! f) { + QString name = QStringLiteral("__qmljs_native_"); + if (function->name) { + if (*function->name == QStringLiteral("%entry")) + name = *function->name; + else + name += *function->name; + } + f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal + qPrintable(name), _llvmModule); + } + return f; +} + +llvm::Function *InstructionSelection::compileLLVMFunction(IR::Function *function) +{ + llvm::Function *llvmFunction = getLLVMFunction(function); + + QHash<IR::BasicBlock *, llvm::BasicBlock *> blockMap; + QVector<llvm::Value *> tempMap; + + qSwap(_llvmFunction, llvmFunction); + qSwap(_function, function); + qSwap(_tempMap, tempMap); + qSwap(_blockMap, blockMap); + + // create the LLVM blocks + foreach (IR::BasicBlock *block, _function->basicBlocks) + (void) getLLVMBasicBlock(block); + + // entry block + SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); + + llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), + getInt32Ty(), "", GetInsertBlock()); + qSwap(_allocaInsertPoint, allocaInsertPoint); + + for (int i = 0; i < _function->tempCount; ++i) { + llvm::AllocaInst *t = newLLVMTemp(_valueTy); + _tempMap.append(t); + } + + foreach (llvm::Value *t, _tempMap) { + CreateStore(llvm::Constant::getNullValue(_valueTy), t); + } + +// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"), +// _llvmFunction->arg_begin()); + + foreach (IR::BasicBlock *block, _function->basicBlocks) { + qSwap(_block, block); + SetInsertPoint(getLLVMBasicBlock(_block)); + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + qSwap(_block, block); + } + + qSwap(_allocaInsertPoint, allocaInsertPoint); + + allocaInsertPoint->eraseFromParent(); + + qSwap(_blockMap, blockMap); + qSwap(_tempMap, tempMap); + qSwap(_function, function); + qSwap(_llvmFunction, llvmFunction); + + // Validate the generated code, checking for consistency. + llvm::verifyFunction(*llvmFunction); + // Optimize the function. + if (_fpm) + _fpm->run(*llvmFunction); + + return llvmFunction; +} + +llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) +{ + llvm::BasicBlock *&llvmBlock = _blockMap[block]; + if (! llvmBlock) + llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), + _llvmFunction); + return llvmBlock; +} + +llvm::Value *InstructionSelection::getLLVMTempReference(IR::Expr *expr) +{ + if (IR::Temp *t = expr->asTemp()) + return getLLVMTemp(t); + + assert(!"TODO!"); + llvm::Value *addr = newLLVMTemp(_valueTy); +// CreateStore(getLLVMValue(expr), addr); + return addr; +} + +llvm::Value *InstructionSelection::getLLVMCondition(IR::Expr *expr) +{ + llvm::Value *value = 0; + if (IR::Temp *t = expr->asTemp()) { + value = getLLVMTemp(t); + } else { + assert(!"TODO!"); + Q_UNREACHABLE(); + +#if 0 + value = getLLVMValue(expr); + if (! value) { + Q_UNIMPLEMENTED(); + return getInt1(false); + } + + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateStore(value, tmp); + value = tmp; +#endif + } + + return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"), + _llvmFunction->arg_begin(), + value); +} + +llvm::Value *InstructionSelection::getLLVMTemp(IR::Temp *temp) +{ + if (temp->index < 0) { + const int index = -temp->index -1; + return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"), + _llvmFunction->arg_begin(), getInt32(index)); + } + + return _tempMap[temp->index]; +} + +llvm::Value *InstructionSelection::getStringPtr(const QString &s) +{ + llvm::Value *&value = _stringMap[s]; + if (! value) { + const QByteArray bytes = s.toUtf8(); + value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); + _stringMap[s] = value; + } + return value; +} + +llvm::Value *InstructionSelection::getIdentifier(const QString &s) +{ + llvm::Value *str = getStringPtr(s); + llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"), + _llvmFunction->arg_begin(), str); + return id; +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + CreateBr(getLLVMBasicBlock(s->target)); +} + +void InstructionSelection::visitCJump(IR::CJump *s) +{ + CreateCondBr(getLLVMCondition(s->cond), + getLLVMBasicBlock(s->iftrue), + getLLVMBasicBlock(s->iffalse)); +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + IR::Temp *t = s->expr->asTemp(); + assert(t != 0); + llvm::Value *result = getLLVMTemp(t); + llvm::Value *ctx = _llvmFunction->arg_begin(); + CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result); + CreateRetVoid(); +} + +void InstructionSelection::visitTry(IR::Try *) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +#if 0 +void InstructionSelection::visitString(IR::String *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"), + _llvmFunction->arg_begin(), tmp, + getStringPtr(*e->value)); + _llvmValue = CreateLoad(tmp); +} +#endif + +llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) +{ + llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); + return addr; +} + +llvm::Value * InstructionSelection::genArguments(IR::ExprList *exprs, int &argc) +{ + llvm::Value *args = 0; + + argc = 0; + for (IR::ExprList *it = exprs; it; it = it->next) + ++argc; + + if (argc) + args = newLLVMTemp(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + int i = 0; + for (IR::ExprList *it = exprs; it; it = it->next) { +// llvm::Value *arg = getLLVMValue(it->expr); +// CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + return args; +} + +void InstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef<llvm::Value *>(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef<llvm::Value *>(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallName(IR::Call *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + switch (base->builtin) { + case IR::Name::builtin_invalid: + break; + + case IR::Name::builtin_typeof: + CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_throw: + CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"), + _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); + _llvmValue = llvm::UndefValue::get(_valueTy); + return; + + case IR::Name::builtin_finish_try: + // ### FIXME. + return; + + case IR::Name::builtin_foreach_iterator_object: + CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_foreach_next_property_name: + CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"), + result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case IR::Name::builtin_delete: { + if (IR::Subscript *subscript = e->args->expr->asSubscript()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(subscript->base), + getLLVMTempReference(subscript->index)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Member *member = e->args->expr->asMember()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(member->base), + getIdentifier(*member->name)); + _llvmValue = CreateLoad(result); + return; + } else if (IR::Name *name = e->args->expr->asName()) { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"), + _llvmFunction->arg_begin(), + result, + getIdentifier(*name->id)); + _llvmValue = CreateLoad(result); + return; + } else { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + } + } break; + + default: + Q_UNREACHABLE(); + } + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +void InstructionSelection::genConstructName(IR::New *e, llvm::Value *result) +{ + IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + Q_UNREACHABLE(); + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +#if 0 +void InstructionSelection::visitCall(IR::Call *e) +{ + if (e->base->asMember()) { + genCallMember(e); + } else if (e->base->asTemp()) { + genCallTemp(e); + } else if (e->base->asName()) { + genCallName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitNew(IR::New *e) +{ + if (e->base->asMember()) { + genConstructMember(e); + } else if (e->base->asTemp()) { + genConstructTemp(e); + } else if (e->base->asName()) { + genConstructName(e); + } else if (IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitSubscript(IR::Subscript *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *index = getLLVMTempReference(e->index); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), result, base, index); + _llvmValue = CreateLoad(result); +} +#endif + +#if 0 +void InstructionSelection::visitMember(IR::Member *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *name = getIdentifier(*e->name); + + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), result, base, name); + _llvmValue = CreateLoad(result); +} +#endif + +llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str) +{ + llvm::Function *func = _llvmModule->getFunction(str); + if (!func) { + std::cerr << "Cannot find runtime function \"" + << str.str() << "\"!" << std::endl; + assert(func); + } + return func; +} + +llvm::Value *InstructionSelection::createValue(IR::Const *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + + switch (e->type) { + case IR::UndefinedType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp); + break; + + case IR::NullType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp); + break; + + case IR::BoolType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp, + getInt1(e->value ? 1 : 0)); + break; + + case IR::NumberType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp, + llvm::ConstantFP::get(_numberTy, e->value)); + break; + + default: + Q_UNREACHABLE(); + } + + return tmp; +} + +llvm::Value *InstructionSelection::toValuePtr(IR::Expr *e) +{ + if (IR::Temp *t = e->asTemp()) { + return getLLVMTemp(t); + } else if (IR::Const *c = e->asConst()) { + return createValue(c); + } else { + Q_UNREACHABLE(); + } +} + +llvm::Value *InstructionSelection::genStringList(const QList<const QString *> &strings, const char *arrayName, const char *elementName) +{ + llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()), + arrayName); + for (int i = 0, ei = strings.size(); i < ei; ++i) { + llvm::Value *el; + if (const QString *string = strings.at(i)) + el = getIdentifier(*string); + else + el = llvm::Constant::getNullValue(_stringPtrTy); + llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName); + CreateStore(el, ptr); + } + + return array; +} diff --git a/src/qml/qml/v4vm/qv4isel_llvm_p.h b/src/qml/qml/v4vm/qv4isel_llvm_p.h new file mode 100644 index 0000000000..00b6527e6a --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_llvm_p.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_LLVM_P_H +#define QV4ISEL_LLVM_P_H + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include <llvm/Module.h> +#include <llvm/PassManager.h> +#include <llvm/IRBuilder.h> + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include "qv4isel_p.h" +#include "qv4jsir_p.h" + +namespace QQmlJS { +namespace LLVM { + +class InstructionSelection: + public llvm::IRBuilder<>, + public IR::InstructionSelection +{ +public: + InstructionSelection(llvm::LLVMContext &context); + + void buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); + +public: // methods from InstructionSelection: + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *con, IR::Temp *temp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Temp *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *sourceBase, const QString &sourceName, IR::Temp *target); + virtual void setProperty(IR::Temp *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *sourceBase, IR::Temp *sourceIndex, IR::Temp *target); + virtual void setElement(IR::Temp *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Temp *leftSource, IR::Temp *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Temp *source, IR::Temp *targetBase, const QString &targetName); + +public: // visitor methods for StmtVisitor: + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + virtual void visitTry(IR::Try *); + +private: + llvm::Function *getRuntimeFunction(llvm::StringRef str); + llvm::Function *getLLVMFunction(IR::Function *function); + llvm::Function *compileLLVMFunction(IR::Function *function); + llvm::BasicBlock *getLLVMBasicBlock(IR::BasicBlock *block); + llvm::Value *getLLVMTempReference(IR::Expr *expr); + llvm::Value *getLLVMCondition(IR::Expr *expr); + llvm::Value *getLLVMTemp(IR::Temp *temp); + llvm::Value *getStringPtr(const QString &s); + llvm::Value *getIdentifier(const QString &s); + llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); + llvm::Value * genArguments(IR::ExprList *args, int &argc); + void genCallTemp(IR::Call *e, llvm::Value *result = 0); + void genCallName(IR::Call *e, llvm::Value *result = 0); + void genCallMember(IR::Call *e, llvm::Value *result = 0); + void genConstructTemp(IR::New *e, llvm::Value *result = 0); + void genConstructName(IR::New *e, llvm::Value *result = 0); + void genConstructMember(IR::New *e, llvm::Value *result = 0); + llvm::Value *createValue(IR::Const *e); + llvm::Value *toValuePtr(IR::Expr *e); + llvm::Value *genStringList(const QList<const QString *> &strings, + const char *arrayName, const char *elementName); + + +private: + llvm::Module *_llvmModule; + llvm::Function *_llvmFunction; + llvm::Value *_llvmValue; + llvm::Type *_numberTy; + llvm::Type *_valueTy; + llvm::Type *_contextPtrTy; + llvm::Type *_stringPtrTy; + llvm::FunctionType *_functionTy; + llvm::Instruction *_allocaInsertPoint; + IR::Function *_function; + IR::BasicBlock *_block; + QHash<IR::Function *, llvm::Function *> _functionMap; + QHash<IR::BasicBlock *, llvm::BasicBlock *> _blockMap; + QVector<llvm::Value *> _tempMap; + QHash<QString, llvm::Value *> _stringMap; + llvm::FunctionPassManager *_fpm; +}; + +} // LLVM namespace +} // QQmlJS namespace + +#endif // QV4ISEL_LLVM_P_H diff --git a/src/qml/qml/v4vm/qv4isel_masm.cpp b/src/qml/qml/v4vm/qv4isel_masm.cpp new file mode 100644 index 0000000000..a9d41ca32e --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_masm.cpp @@ -0,0 +1,1252 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_masm_p.h" +#include "qv4runtime.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include "qv4regexpobject.h" +#include "qv4unwindhelper.h" +#include "qv4lookup.h" + +#include <assembler/LinkBuffer.h> +#include <WTFStubs.h> + +#include <iostream> +#include <cassert> + +#if USE(UDIS86) +# include <udis86.h> +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QQmlJS::VM; + +/* Platform/Calling convention/Architecture specific section */ + +#if CPU(X86_64) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::rbx, + // Not used: JSC::X86Registers::r10, + JSC::X86Registers::r12, // LocalsRegister + // Not used: JSC::X86Registers::r13, + JSC::X86Registers::r14 // ContextRegister + // Not used: JSC::X86Registers::r15, +}; +#endif + +#if CPU(X86) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::ebx, + JSC::X86Registers::esi, // ContextRegister + JSC::X86Registers::edi // LocalsRegister +}; +#endif + +#if CPU(ARM) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // ### FIXME: remove unused registers. + // Keep these in reverse order and make sure to also edit the unwind program in + // qv4unwindhelper_p-arm.h when changing this list. + JSC::ARMRegisters::r12, + JSC::ARMRegisters::r10, + JSC::ARMRegisters::r9, + JSC::ARMRegisters::r8, + JSC::ARMRegisters::r7, + JSC::ARMRegisters::r6, + JSC::ARMRegisters::r5, + JSC::ARMRegisters::r4 +}; +#endif + +const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +/* End of platform/calling convention/architecture specific section */ + + +const Assembler::VoidType Assembler::Void; + +Assembler::Assembler(V4IR::Function* function, VM::Function *vmFunction, VM::ExecutionEngine *engine) + : _function(function), _vmFunction(vmFunction), _engine(engine) +{ +} + +void Assembler::registerBlock(V4IR::BasicBlock* block) +{ + _addrs[block] = label(); +} + +void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target) +{ + if (current->index + 1 != target->index) + _patches[target].append(jump()); +} + +void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + +void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target) +{ + _labelPatches[target].append(patch); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t) +{ + int32_t offset = 0; + int scope = t->scope; + VM::Function *f = _vmFunction; + RegisterID context = ContextRegister; + if (scope) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); + --scope; + f = f->outer; + context = ScratchRegister; + while (scope) { + loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); + f = f->outer; + --scope; + } + } + if (t->index < 0) { + const int arg = -t->index - 1; + loadPtr(Address(context, offsetof(CallContext, arguments)), reg); + offset = arg * sizeof(Value); + } else if (t->index < f->locals.size()) { + loadPtr(Address(context, offsetof(CallContext, locals)), reg); + offset = t->index * sizeof(Value); + } else { + assert(t->scope == 0); + const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size() + 1; + offset = - sizeof(Value) * (arg + 1); + offset -= sizeof(void*) * calleeSavedRegisterCount; + reg = LocalsRegister; + } + return Pointer(reg, offset); +} + +template <typename Result, typename Source> +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +template <typename Result> +void Assembler::copyValue(Result result, V4IR::Expr* source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgument(source, ReturnValueRegister); + storeArgument(ReturnValueRegister, result); +#else + if (V4IR::Temp *temp = source->asTemp()) { + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); + } else if (V4IR::Const *c = source->asConst()) { + VM::Value v = convertToValue(c); + storeValue(v, result); + } else { + assert(! "not implemented"); + } +#endif +} + + +void Assembler::storeValue(VM::Value value, V4IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + +void Assembler::enterStandardStackFrame(int locals) +{ + platformEnterStandardStackFrame(); + + // ### FIXME: Handle through calleeSavedRegisters mechanism + // or eliminate StackFrameRegister altogether. + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + // space for the locals and callee saved registers + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount; + +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + subPtr(TrustedImm32(frameSize), StackPointerRegister); + + for (int i = 0; i < calleeSavedRegisterCount; ++i) + storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + + move(StackFrameRegister, LocalsRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the callee saved registers + for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); + + // space for the locals and the callee saved registers + int32_t frameSize = locals * sizeof(QQmlJS::VM::Value) + sizeof(void*) * calleeSavedRegisterCount; +#if CPU(X86) || CPU(X86_64) + frameSize = (frameSize + 15) & ~15; // align on 16 byte boundaries for MMX +#endif + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. +#if CPU(ARM_THUMB2) + move(TrustedImm32(frameSize), Assembler::ScratchRegister); + add32(Assembler::ScratchRegister, StackPointerRegister); +#else + addPtr(TrustedImm32(frameSize), StackPointerRegister); +#endif + + pop(StackFrameRegister); + platformLeaveStandardStackFrame(); +} + + + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0 } + +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement + + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor + + INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul + + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OP(__qmljs_instanceof), // OpInstanceof + OP(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + +void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(VM::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(VM::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(VM::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(VM::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(VM::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(VM::Value, tag); + store32(TrustedImm32(VM::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} +#if OS(LINUX) +static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions, + const QVector<String*> &identifiers) +{ + QByteArray processedOutput(output); + for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + for (QVector<String*>::ConstIterator it = identifiers.begin(), end = identifiers.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(*it), 16); + ptrString.prepend("0x"); + QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\""; + processedOutput = processedOutput.replace(ptrString, replacement); + } + fprintf(stderr, "%s\n", processedOutput.constData()); +} +#endif + +void Assembler::link(VM::Function *vmFunc) +{ + Label endOfCode = label(); +#ifdef Q_PROCESSOR_ARM + // Let the ARM exception table follow right after that + for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i) + nop(); +#endif + + { + QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + } + + JSC::JSGlobalData dummy(_engine->executableAllocator); + JSC::LinkBuffer linkBuffer(dummy, this, 0); + vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); + + QHash<void*, const char*> functions; + foreach (CallToLink ctl, _callsToLink) { + linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + + { + QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (DataLabelPtr label, it.value()) + linkBuffer.patch(label, linkBuffer.locationOf(target)); + } + } + +#ifdef Q_PROCESSOR_ARM + UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode)); +#endif + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#if OS(LINUX) + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); +#endif + + QByteArray name = _function->name->toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(_function), 16); + name.prepend("IR::Function(0x"); + name.append(")"); + } + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); +#if OS(LINUX) + fclose(disasmStream); +#if CPU(X86) || CPU(X86_64) + QHash<void*, String*> idents; + printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers); +#endif + free(disasmOutput); +#endif + } else { + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + vmFunc->code = (Value (*)(VM::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); +} + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _block(0) + , _function(0) + , _vmFunction(0) + , _as(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ + delete _as; +} + +void InstructionSelection::run(VM::Function *vmFunction, V4IR::Function *function) +{ + QVector<Lookup> lookups; + QSet<V4IR::BasicBlock*> reentryBlocks; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + Assembler* oldAssembler = _as; + _as = new Assembler(_function, _vmFunction, engine()); + + int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) + 1; + locals = (locals + 1) & ~1; + _as->enterStandardStackFrame(locals); + + int contextPointer = 0; +#ifndef VALUE_FITS_IN_REGISTER + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif + +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#endif + + foreach (V4IR::BasicBlock *block, _function->basicBlocks) { + _block = block; + _as->registerBlock(_block); + + if (_reentryBlocks.contains(_block)) { + _as->enterStandardStackFrame(/*locals*/0); +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); + _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); +#else + _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); +#endif + } + + foreach (V4IR::Stmt *s, block->statements) { + s->accept(this); + } + } + + _as->leaveStandardStackFrame(locals); +#ifndef ARGUMENTS_IN_REGISTERS + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _as->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _as->poke(Assembler::ScratchRegister); +#endif + _as->ret(); + + _as->link(_vmFunction); + + if (_lookups.size()) { + _vmFunction->lookups = new Lookup[_lookups.size()]; + memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); + } + + UnwindHelper::registerFunction(_vmFunction); + + qSwap(_vmFunction, vmFunction); + qSwap(_function, function); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + delete _as; + _as = oldAssembler; +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + VM::String *s = identifier(*func->id); + + if (useFastLookups && func->global) { + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_activation_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value)); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + _as->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_throw, Assembler::ContextRegister, Assembler::Reference(arg)); +} + +typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); +static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, + VM::String *exceptionVarName, Value *exceptionVar) +{ + *exceptionVar = Value::undefinedValue(); + void *addressToContinueAt = 0; + try { + addressToContinueAt = tryBody(context, localsPtr); + } catch (Exception& ex) { + ex.accept(context); + *exceptionVar = ex.value(); + try { + ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context); + addressToContinueAt = catchBody(catchContext, localsPtr); + context = __qmljs_builtin_pop_scope(catchContext); + } catch (Exception& ex) { + *exceptionVar = ex.value(); + ex.accept(context); + addressToContinueAt = catchBody(context, localsPtr); + } + } + return addressToContinueAt; +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end + // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper + // returns and to which we jump to. + + _reentryBlocks.insert(t->tryBlock); + _reentryBlocks.insert(t->catchBlock); + + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, + Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), + identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); + _as->jump(Assembler::ReturnValueRegister); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper + // with the address that we'd like to continue at, which is right after the ret below. + Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); + _as->leaveStandardStackFrame(/*locals*/0); + _as->ret(); + _as->addPatch(continuation, _as->label()); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopScope() +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + int length = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, + Assembler::PointerToValue(result), + baseAddressForCallArguments(), Assembler::TrustedImm32(length)); +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + V4IR::Temp* thisObject = 0; + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), + Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ +#if defined(VALUE_FITS_IN_REGISTER) + _as->loadPtr(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister); + _as->storeArgument(Assembler::ReturnValueRegister, temp); +#else + _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject))); +#endif +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + _as->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Value v = Value::fromString(identifier(str)); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + String *propertyName = identifier(*name->id); + if (useFastLookups && name->global) { + uint index = addGlobalLookup(propertyName); + generateFunctionCall(Assembler::Void, __qmljs_get_global_lookup, Assembler::ContextRegister, Assembler::PointerToValue(temp), + Assembler::TrustedImm32(index)); + return; + } + generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, + Assembler::ContextRegister, propertyName, Assembler::Reference(source)); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + if (useFastLookups) { + VM::String *s = identifier(name); + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_get_property_lookup, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::Reference(base), Assembler::TrustedImm32(index)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::Reference(base), identifier(name)); + } +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + if (useFastLookups) { + VM::String *s = identifier(targetName); + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_set_property_lookup, + Assembler::ContextRegister, Assembler::Reference(targetBase), + Assembler::TrustedImm32(index), Assembler::Reference(source)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, + Assembler::Reference(targetBase), + identifier(targetName), Assembler::Reference(source)); + } +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, + Assembler::Reference(targetBase), Assembler::Reference(targetIndex), + Assembler::Reference(source)); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + _as->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + VM::UnaryOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: setOp(op, opName, __qmljs_not); break; + case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), + Assembler::Reference(sourceTemp)); +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target) +{ + _as->generateBinOp(oper, target, leftSource, rightSource); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + VM::InplaceBinOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + identifier(targetName), Assembler::Reference(rightSource)); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + VM::InplaceBinOpElement op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), + Assembler::Reference(source)); + } +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + VM::InplaceBinOpMember op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBase), identifier(targetName), + Assembler::Reference(source)); + } +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, + V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + VM::String *s = identifier(name); + + if (useFastLookups) { + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_call_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(func != 0); + + if (useFastLookups && func->global) { + int argc = prepareVariableArguments(args); + VM::String *s = identifier(*func->id); + + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + return; + } + + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(value != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + _as->jumpToBlock(_block, s->target); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + if (V4IR::Temp *t = s->cond->asTemp()) { + Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); + Address tag = temp; + tag.offset += offsetof(VM::Value, tag); + Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(VM::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(VM::Value, int_32); + _as->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _as->jump(); + + booleanConversion.link(_as); + { + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); + } + + testBoolean.link(_as); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else if (V4IR::Binop *b = s->cond->asBinop()) { + if (b->left->asTemp() && b->right->asTemp()) { + VM::CmpOp op = 0; + const char *opName = 0; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case V4IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break; + case V4IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; + } // switch + + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, Assembler::ContextRegister, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + if (V4IR::Temp *t = s->expr->asTemp()) { +#if defined(ARGUMENTS_IN_REGISTERS) && defined(VALUE_FITS_IN_REGISTER) + _as->copyValue(Assembler::ReturnValueRegister, t); +#else + _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); +#endif + return; + } + Q_UNIMPLEMENTED(); + Q_UNUSED(s); +} + +int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args) +{ + int argc = 0; + for (V4IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (V4IR::ExprList *it = args; it; it = it->next, ++i) { +// V4IR::Temp *arg = it->expr->asTemp(); +// assert(arg != 0); + _as->copyValue(argumentAddressForCall(i), it->expr); + } + + return argc; +} + +void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args) +{ + V4IR::Name *baseName = base->asName(); + assert(baseName != 0); + + int argc = prepareVariableArguments(args); + _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), + identifier(*baseName->id), baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + + +uint InstructionSelection::addLookup(VM::String *name) +{ + uint index = (uint)_lookups.size(); + VM::Lookup l; + l.lookupProperty = Lookup::lookupPropertyGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addGlobalLookup(VM::String *name) +{ + uint index = (uint)_lookups.size(); + VM::Lookup l; + l.lookupGlobal = Lookup::lookupGlobalGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} diff --git a/src/qml/qml/v4vm/qv4isel_masm_p.h b/src/qml/qml/v4vm/qv4isel_masm_p.h new file mode 100644 index 0000000000..9f8704725f --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_masm_p.h @@ -0,0 +1,894 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "qv4global.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4object.h" +#include "qv4runtime.h" + +#include <QtCore/QHash> +#include <config.h> +#include <wtf/Vector.h> +#include <assembler/MacroAssembler.h> + +namespace QQmlJS { +namespace MASM { + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(V4IR::Function* function, VM::Function *vmFunction, VM::ExecutionEngine *engine); +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } + + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 8; + + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast<RegisterID>(JSC::ARMRegisters::r0 + index); + }; + inline void platformEnterStandardStackFrame() + { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + pop(JSC::ARMRegisters::r0); + pop(JSC::ARMRegisters::r1); + pop(JSC::ARMRegisters::r2); + pop(JSC::ARMRegisters::r3); + } +#else +#error Argh. +#endif + static const int calleeSavedRegisterCount; + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + struct VoidType { VoidType() {} }; + static const VoidType Void; + + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + struct Reference { + Reference(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + + struct ReentryBlock { + ReentryBlock(V4IR::BasicBlock *b) : block(b) {} + V4IR::BasicBlock *block; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void registerBlock(V4IR::BasicBlock*); + void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); + void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); + void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); + + Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t); + + void loadArgument(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgument(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgument(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgument(PointerToValue temp, RegisterID dest) + { + if (!temp.value) { + loadArgument(TrustedImmPtr(0), dest); + } else { + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } + } + + void loadArgument(Reference temp, RegisterID dest) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgument(addr, dest); + } + + void loadArgument(ReentryBlock block, RegisterID dest) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); + addPatch(patch, block.block); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgument(V4IR::Temp* temp, RegisterID dest) + { + if (!temp) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgument(V4IR::Const* c, RegisterID dest) + { + VM::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgument(V4IR::Expr* expr, RegisterID dest) + { + if (!expr) { + VM::Value undefined = VM::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgument(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgument(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgument(V4IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgument(VM::String* string, RegisterID dest) + { + loadArgument(TrustedImmPtr(string), dest); + } + + void loadArgument(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeArgument(RegisterID src, V4IR::Temp *temp) + { + if (temp) { + Pointer addr = loadTempAddress(ScratchRegister, temp); +#ifdef VALUE_FITS_IN_REGISTER + store64(src, addr); +#else + // If the value doesn't fit into a register, then the + // register contains the address to where the argument + // (return value) is stored. Copy it from there. + copyValue(addr, Pointer(src, 0)); +#endif + } + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeArgument(RegisterID src, const Pointer &dest) + { + store64(src, dest); + } +#endif + + void storeArgument(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void storeArgument(RegisterID, VoidType) + { + } + + using JSC::MacroAssembler::push; + + void push(const Pointer& ptr) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + push(ScratchRegister); + } + + void push(VM::Value value) + { +#ifdef VALUE_FITS_IN_REGISTER + move(TrustedImm64(value.val), ScratchRegister); + push(ScratchRegister); +#else + move(TrustedImm32(value.tag), ScratchRegister); + push(ScratchRegister); + move(TrustedImm32(value.int_32), ScratchRegister); + push(ScratchRegister); +#endif + } + + void push(PointerToValue temp) + { + if (temp.value) { + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + push(ptr); + } else { + push(TrustedImmPtr(0)); + } + } + + void push(Reference temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + push(ptr); + } + + void push(ReentryBlock block) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); + push(ScratchRegister); + addPatch(patch, block.block); + } + + void push(V4IR::Temp* temp) + { + if (temp) { + Address addr = loadTempAddress(ScratchRegister, temp); + addr.offset += 4; + push(addr); + addr.offset -= 4; + push(addr); + } else { + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); + } + } + + void push(V4IR::Const* c) + { + VM::Value v = convertToValue(c); + push(v); + } + + void push(V4IR::Expr* e) + { + if (!e) { + VM::Value undefined = VM::Value::undefinedValue(); + push(undefined); + } else if (V4IR::Const *c = e->asConst()) + push(c); + else if (V4IR::Temp *t = e->asTemp()) { + push(t); + } else { + assert(!"Trying to push an expression that is not a Temp or Const"); + } + } + + void push(TrustedImmPtr ptr) + { + move(TrustedImmPtr(ptr), ScratchRegister); + push(ScratchRegister); + } + + void push(VM::String* name) + { + push(TrustedImmPtr(name)); + } + + using JSC::MacroAssembler::loadDouble; + void loadDouble(V4IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, V4IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template <typename Result, typename Source> + void copyValue(Result result, Source source); + template <typename Result> + void copyValue(Result result, V4IR::Expr* source); + + void storeValue(VM::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(VM::Value value, V4IR::Temp* temp); + + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + + static inline int sizeOfArgument(VoidType) + { return 0; } + static inline int sizeOfArgument(RegisterID) + { return RegisterSize; } + static inline int sizeOfArgument(V4IR::Temp*) + { return 8; } // Size of value + static inline int sizeOfArgument(V4IR::Expr*) + { return 8; } // Size of value + static inline int sizeOfArgument(const Pointer&) + { return sizeof(void*); } + static inline int sizeOfArgument(VM::String*) + { return sizeof(VM::String*); } + static inline int sizeOfArgument(const PointerToValue &) + { return sizeof(void *); } + static inline int sizeOfArgument(const Reference &) + { return sizeof(void *); } + static inline int sizeOfArgument(const ReentryBlock &) + { return sizeof(void *); } + static inline int sizeOfArgument(TrustedImmPtr) + { return sizeof(void*); } + static inline int sizeOfArgument(TrustedImm32) + { return 4; } + + template <int argumentNumber, typename T> + int loadArgumentOnStackOrRegister(const T &value) + { + if (argumentNumber < RegisterArgumentCount) { + loadArgument(value, registerForArgument(argumentNumber)); + return 0; + } else { + push(value); + return sizeOfArgument(value); + } + } + + template <int argumentNumber> + int loadArgumentOnStackOrRegister(const VoidType &value) + { + Q_UNUSED(value); + return 0; + } + + template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + int totalNumberOfArgs = 6; + + // If necessary reserve space for the return value on the stack and + // pass the pointer to it as the first hidden parameter. + bool returnValueOnStack = false; + int sizeOfReturnValueOnStack = sizeOfArgument(r); + if (sizeOfReturnValueOnStack > RegisterSize) { + sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister); + ++totalNumberOfArgs; + returnValueOnStack = true; + } + + int stackSpaceUsedForArgs = 0; + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<5>(arg6); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<4>(arg5); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<3>(arg4); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<2>(arg3); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<1>(arg2); + stackSpaceUsedForArgs += loadArgumentOnStackOrRegister<0>(arg1); + + if (returnValueOnStack) { + // Load address of return value + push(Pointer(StackPointerRegister, stackSpaceUsedForArgs)); + } + + callAbsolute(functionName, function); + + int stackSizeToCorrect = stackSpaceUsedForArgs; + if (returnValueOnStack) + stackSizeToCorrect += sizeOfReturnValueOnStack; + + storeArgument(ReturnValueRegister, r); + + if (stackSizeToCorrect) + add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister); + } + + template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + + template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template <typename ArgRet, typename Arg1, typename Arg2> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template <typename ArgRet, typename Arg1> + void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + VM::BinOp fallbackImplementation; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; + + void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right); + + Jump inline_add32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_and32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + void link(VM::Function *vmFunc); + +private: + V4IR::Function *_function; + VM::Function *_vmFunction; + QHash<V4IR::BasicBlock *, Label> _addrs; + QHash<V4IR::BasicBlock *, QVector<Jump> > _patches; + QList<CallToLink> _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList<DataLabelPatch> _dataLabelPatches; + + QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches; + + VM::ExecutionEngine *_engine; +}; + +class Q_V4_EXPORT InstructionSelection: + protected V4IR::InstructionSelection, + public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(VM::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::LocalsRegister, sizeof(VM::Value) * (-index) + - sizeof(void*) // size of ebp + - sizeof(void*) * Assembler::calleeSavedRegisterCount + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + VM::String *identifier(const QString &s); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(V4IR::ExprList* args); + + typedef void (*ActivationMethod)(VM::ExecutionContext *, VM::Value *result, VM::String *name, VM::Value *args, int argc); + void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + uint addLookup(VM::String *name); + uint addGlobalLookup(VM::String *name); + + V4IR::BasicBlock *_block; + V4IR::Function* _function; + VM::Function* _vmFunction; + QVector<VM::Lookup> _lookups; + Assembler* _as; + QSet<V4IR::BasicBlock*> _reentryBlocks; +}; + +class Q_V4_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/qml/v4vm/qv4isel_p.cpp b/src/qml/qml/v4vm/qv4isel_p.cpp new file mode 100644 index 0000000000..417e38550e --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_p.cpp @@ -0,0 +1,398 @@ +#include "debugging.h" +#include "qv4engine.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4functionobject.h" + +#include <QString> + +#include <cassert> + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + +using namespace QQmlJS; +using namespace QQmlJS::V4IR; + +EvalInstructionSelection::EvalInstructionSelection(VM::ExecutionEngine *engine, Module *module) + : _engine(engine) + , useFastLookups(true) +{ + assert(engine); + assert(module); + + createFunctionMapping(0, module->rootFunction); + foreach (V4IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } +} + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EvalISelFactory::~EvalISelFactory() +{} + +VM::Function *EvalInstructionSelection::createFunctionMapping(VM::Function *outer, Function *irFunction) +{ + VM::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); + vmFunction->isStrict = irFunction->isStrict; + vmFunction->outer = outer; + vmFunction->isNamedExpression = irFunction->isNamedExpression; + + if (outer) + outer->nestedFunctions.append(vmFunction); + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(_engine->newString(*formal)); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(_engine->newString(*local)); + + foreach (V4IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(vmFunction, function); + + if (_engine->debugger) + _engine->debugger->mapFunction(vmFunction, irFunction); + + return vmFunction; +} + +VM::Function *EvalInstructionSelection::vmFunction(Function *f) { + VM::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} + +void InstructionSelection::visitMove(V4IR::Move *s) +{ + if (s->op == V4IR::OpInvalid) { + if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + setActivationProperty(s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Temp *t = s->target->asTemp()) { + if (V4IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(n, t); + return; + } else if (V4IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (V4IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (V4IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (V4IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (V4IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (V4IR::New *ctor = s->source->asNew()) { + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); + return; + } else if (V4IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); + return; + } else if (V4IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); + return; + } + } else if (V4IR::Member *m = s->source->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (V4IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (V4IR::Unop *u = s->source->asUnop()) { + if (V4IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (V4IR::Binop *b = s->source->asBinop()) { + if (b->left->asTemp() && b->right->asTemp()) { + binop(b->op, b->left->asTemp(), b->right->asTemp(), t); + return; + } + } else if (V4IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callBuiltin(c, t); + return; + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, t); + return; + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, t); + return; + } else if (V4IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); + return; + } + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp()) { + setProperty(s->source->asTemp(), base, *m->name); + return; + } + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (V4IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp()) { + binop(s->op, t, s->source->asTemp(), t); + return; + } + } else if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + inplaceNameOp(s->op, s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (s->source->asTemp()) { + inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, V4IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::visitEnter(Enter *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitLeave(Leave *) +{ + Q_UNREACHABLE(); +} + +void InstructionSelection::visitExp(V4IR::Exp *s) +{ + if (V4IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callBuiltin(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + callProperty(member->base, *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + callSubscript(s->base, s->index, c->args, 0); + } else { + Q_UNIMPLEMENTED(); + } + } else { + Q_UNIMPLEMENTED(); + } +} + +void InstructionSelection::callBuiltin(V4IR::Call *call, V4IR::Temp *result) +{ + V4IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case V4IR::Name::builtin_invalid: + callBuiltinInvalid(baseName, call->args, result); + return; + + case V4IR::Name::builtin_typeof: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_delete: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case V4IR::Name::builtin_postincrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_postdecrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_throw: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case V4IR::Name::builtin_finish_try: + callBuiltinFinishTry(); + return; + + case V4IR::Name::builtin_foreach_iterator_object: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case V4IR::Name::builtin_foreach_next_property_name: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case V4IR::Name::builtin_push_with_scope: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWithScope(arg); + } return; + + case V4IR::Name::builtin_pop_scope: + callBuiltinPopScope(); + return; + + case V4IR::Name::builtin_declare_vars: { + if (!call->args) + return; + V4IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == V4IR::BoolType); + for (V4IR::ExprList *it = call->args->next; it; it = it->next) { + V4IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case V4IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + V4IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case V4IR::Name::builtin_define_property: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + case V4IR::Name::builtin_define_array: + callBuiltinDefineArray(result, call->args); + return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/src/qml/qml/v4vm/qv4isel_p.h b/src/qml/qml/v4vm/qv4isel_p.h new file mode 100644 index 0000000000..e85127c5a9 --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "qv4global.h" +#include "qv4jsir_p.h" + +#include <qglobal.h> +#include <QHash> + +namespace QQmlJS { + +namespace VM { +struct ExecutionEngine; +struct Function; +} // namespace VM + +class Q_V4_EXPORT EvalInstructionSelection +{ +public: + EvalInstructionSelection(VM::ExecutionEngine *engine, V4IR::Module *module); + virtual ~EvalInstructionSelection() = 0; + + VM::Function *vmFunction(V4IR::Function *f); + + void setUseFastLookups(bool b) { useFastLookups = b; } + +protected: + VM::Function *createFunctionMapping(VM::Function *outer, V4IR::Function *irFunction); + VM::ExecutionEngine *engine() const { return _engine; } + virtual void run(VM::Function *vmFunction, V4IR::Function *function) = 0; + +private: + VM::ExecutionEngine *_engine; + QHash<V4IR::Function *, VM::Function *> _irToVM; +protected: + bool useFastLookups; +}; + +class Q_V4_EXPORT EvalISelFactory +{ +public: + virtual ~EvalISelFactory() = 0; + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, V4IR::Module *module) = 0; +}; + +namespace V4IR { +class Q_V4_EXPORT InstructionSelection: protected V4IR::StmtVisitor +{ +public: + virtual ~InstructionSelection() = 0; + +public: // visitor methods for StmtVisitor: + virtual void visitMove(V4IR::Move *s); + virtual void visitEnter(V4IR::Enter *); + virtual void visitLeave(V4IR::Leave *); + virtual void visitExp(V4IR::Exp *s); + +public: // to implement by subclasses: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinThrow(V4IR::Temp *arg) = 0; + virtual void callBuiltinFinishTry() = 0; + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0; + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void loadThisObject(V4IR::Temp *temp) = 0; + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0; + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0; + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0; + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0; + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0; + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void binop(V4IR::AluOp oper, V4IR::Temp *leftSource, V4IR::Temp *rightSource, V4IR::Temp *target) = 0; + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0; + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(V4IR::Call *c, V4IR::Temp *temp); +}; +} // namespace IR + +} // namespace QQmlJS + +#endif // QV4ISEL_P_H diff --git a/src/qml/qml/v4vm/qv4isel_util_p.h b/src/qml/qml/v4vm/qv4isel_util_p.h new file mode 100644 index 0000000000..e10a9658f2 --- /dev/null +++ b/src/qml/qml/v4vm/qv4isel_util_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "qv4runtime.h" +#include "qv4jsir_p.h" + +namespace QQmlJS { + +inline VM::Value convertToValue(V4IR::Const *c) +{ + switch (c->type) { + case V4IR::MissingType: + return VM::Value::deletedValue(); + case V4IR::NullType: + return VM::Value::nullValue(); + case V4IR::UndefinedType: + return VM::Value::undefinedValue(); + case V4IR::BoolType: + return VM::Value::fromBoolean(c->value != 0); + case V4IR::NumberType: { + int ival = (int)c->value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { + return VM::Value::fromInt32(ival); + } else { + return VM::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/qml/v4vm/qv4jsir.cpp b/src/qml/qml/v4vm/qv4jsir.cpp new file mode 100644 index 0000000000..3dad094624 --- /dev/null +++ b/src/qml/qml/v4vm/qv4jsir.cpp @@ -0,0 +1,948 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jsir_p.h" +#include <private/qqmljsast_p.h> + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> +#include <QtCore/qset.h> +#include <cmath> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace V4IR { + +const char *typeName(Type t) +{ + switch (t) { + case UndefinedType: return "undefined"; + case NullType: return "null"; + case BoolType: return "bool"; + case NumberType: return "number"; + default: return "invalid"; + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast<QSOperator::Op>(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; + default: return OpInvalid; + } +} + +struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor +{ + CloneExpr clone; + QSet<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(V4IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template <typename _Expr> + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + V4IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitEnter(Enter *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitLeave(Leave *) + { + // nothing to do for Leave statements + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitTry(Try *) + { + // nothing to do for Try statements + } + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + +void Const::dump(QTextStream &out) +{ + switch (type) { + case QQmlJS::V4IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::V4IR::NullType: + out << "null"; + break; + case QQmlJS::V4IR::BoolType: + out << (value ? "true" : "false"); + break; + case QQmlJS::V4IR::MissingType: + out << "missing"; + break; + default: + out << QString::number(value, 'g', 16); + break; + } +} + +void String::dump(QTextStream &out) +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::initGlobal(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = true; + this->line = line; + this->column = column; +} + +void Name::init(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = false; + this->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->global = false; + this->line = line; + this->column = column; +} + +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_finish_try: + return "builtin_finish_try"; + case V4IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case V4IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case V4IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case V4IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case V4IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case V4IR::Name::builtin_define_property: + return "builtin_define_property"; + case V4IR::Name::builtin_define_array: + return "builtin_define_array"; + case V4IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + } + return "builtin_(###FIXME)"; +}; + + +void Name::dump(QTextStream &out) +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) +{ + if (index < 0) { + out << '#' << -(index + 1); // negative and 1-based. + } else { + out << '%' << index; // temp + } + if (scope) + out << "@" << scope; +} + +void Closure::dump(QTextStream &out) +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Unop::dump(QTextStream &out) +{ + out << opname(op); + expr->dump(out); +} + +void Binop::dump(QTextStream &out) +{ + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); +} + +void Call::dump(QTextStream &out) +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Enter::dump(QTextStream &out, Mode) +{ + out << "%enter("; + expr->dump(out); + out << ");"; +} + +void Leave::dump(QTextStream &out, Mode) +{ + out << "%leave"; + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +void Try::dump(QTextStream &out, Stmt::Mode mode) +{ + out << "try L" << tryBlock->index << "; catch exception in "; + exceptionVar->dump(out); + out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, outer, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (V4IR::BasicBlock *b, basicBlocks) + foreach (V4IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +void Function::removeSharedExpressions() +{ + RemoveSharedExpressions removeSharedExpressions; + removeSharedExpressions(this); +} + +int Function::indexOfArgument(const QStringRef &string) const +{ + for (int i = formals.size() - 1; i >= 0; --i) { + if (*formals.at(i) == string) + return i; + } + return -1; +} +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(int index, uint scope) +{ + Temp *e = function->New<Temp>(); + e->init(index, scope); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New<Const>(); + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New<String>(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New<RegExp>(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->initGlobal(function->newString(id), line, column); + return e; +} + + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New<Closure>(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::UNOP(AluOp op, Temp *expr) +{ + Unop *e = function->New<Unop>(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New<Binop>(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New<Call>(); + e->init(base, args); + int argc = 0; + for (ExprList *it = args; it; it = it->next) + ++argc; + function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New<New>(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Temp *base, Temp *index) +{ + Subscript *e = function->New<Subscript>(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Temp *base, const QString *name) +{ + Member*e = function->New<Member>(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New<Exp>(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::ENTER(Expr *expr) +{ + if (isTerminated()) + return 0; + + Enter *s = function->New<Enter>(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::LEAVE() +{ + if (isTerminated()) + return 0; + + Leave *s = function->New<Leave>(); + s->init(); + statements.append(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New<Move>(); + s->init(target, source, op); + statements.append(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New<Jump>(); + s->init(target); + statements.append(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New<CJump>(); + s->init(cond, iftrue, iffalse); + statements.append(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New<Ret>(); + s->init(expr); + statements.append(s); + return s; +} + +Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) +{ + if (isTerminated()) + return 0; + + Try *t = function->New<Try>(); + t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); + statements.append(t); + + assert(! out.contains(tryBlock)); + out.append(tryBlock); + + assert(! out.contains(catchBlock)); + out.append(catchBlock); + + assert(! tryBlock->in.contains(this)); + tryBlock->in.append(this); + + assert(! catchBlock->in.contains(this)); + catchBlock->in.append(this); + + return t; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + out << endl; + } +} + +CloneExpr::CloneExpr(BasicBlock *block) + : block(block), cloned(0) +{ +} + +void CloneExpr::setBasicBlock(BasicBlock *block) +{ + this->block = block; +} + +ExprList *CloneExpr::clone(ExprList *list) +{ + if (! list) + return 0; + + ExprList *clonedList = block->function->New<V4IR::ExprList>(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + cloned = block->TEMP(e->index, e->scope); +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4vm/qv4jsir_p.h b/src/qml/qml/v4vm/qv4jsir_p.h new file mode 100644 index 0000000000..47368449fc --- /dev/null +++ b/src/qml/qml/v4vm/qv4jsir_p.h @@ -0,0 +1,821 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IR_P_H +#define QV4IR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global.h" +#include <private/qqmljsmemorypool_p.h> + +#include <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QBitArray> + +#ifdef CONST +#undef CONST +#endif + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QQmlJS { + +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + +namespace VM { +struct ExecutionContext; +struct Value; +} + +namespace V4IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +struct Closure; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Enter; +struct Leave; +struct Move; +struct Jump; +struct CJump; +struct Ret; +struct Try; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + OpIncrement, + OpDecrement, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpInstanceof, + OpIn, + + OpAnd, + OpOr, + + LastAluOp = OpOr +}; +AluOp binaryOperator(int op); +const char *opname(V4IR::AluOp op); + +enum Type { + MissingType, // Used to indicate holes in array initialization (e.g. [,,]) + UndefinedType, + NullType, + BoolType, + NumberType +}; + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitEnter(Enter *) = 0; + virtual void visitLeave(Leave *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; + virtual void visitTry(Try *) = 0; +}; + +struct Expr { + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + Type type; + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out); + static QString escape(const QString &s); +}; + +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_finish_try, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array, + builtin_define_getter_setter + }; + + const QString *id; + Builtin builtin; + bool global; + quint32 line; + quint32 column; + + void initGlobal(const QString *id, quint32 line, quint32 column); + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Temp: Expr { + int index; + int scope; // how many scopes outside the current one? + + void init(int index, int scope = 0) + { + this->index = index; + this->scope = scope; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Unop: Expr { + AluOp op; + Temp *expr; + + void init(AluOp op, Temp *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Subscript: Expr { + Temp *base; + Temp *index; + + void init(Temp *base, Temp *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Member: Expr { + Temp *base; + const QString *name; + + void init(Temp *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out); +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector<unsigned> uses; + QVector<unsigned> defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + + Stmt(): d(0) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Enter *asEnter() { return 0; } + virtual Leave *asLeave() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual Try *asTry() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Enter: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitEnter(this); } + virtual Enter *asEnter() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Leave: Stmt { + void init() {} + + virtual void accept(StmtVisitor *v) { v->visitLeave(this); } + virtual Leave *asLeave() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Temp *expr; + + void init(Temp *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Try: Stmt { + BasicBlock *tryBlock; + BasicBlock *catchBlock; + QString exceptionVarName; + Temp *exceptionVar; // place to store the caught exception, for use when re-throwing + + void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) + { + this->tryBlock = tryBlock; + this->catchBlock = catchBlock; + this->exceptionVarName = exceptionVarName; + this->exceptionVar = exceptionVar; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitTry(this); } + virtual Try *asTry() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Q_V4_EXPORT Module { + MemoryPool pool; + QVector<Function *> functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector<BasicBlock *> basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet<QString> strings; + QList<const QString *> formals; + QList<const QString *> locals; + QVector<Function *> nestedFunctions; + Function *outer; + + int insideWithOrCatch; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint isNamedExpression : 1; + uint hasTry: 1; + uint hasWith: 1; + uint unused : 26; + + template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, Function *outer, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , outer(outer) + , insideWithOrCatch(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + , hasTry(false) + , hasWith(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlockInsertMode mode = InsertBlock); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); + + int indexOfArgument(const QStringRef &string) const; +}; + +struct BasicBlock { + Function *function; + QVector<Stmt *> statements; + QVector<BasicBlock *> in; + QVector<BasicBlock *> out; + QBitArray liveIn; + QBitArray liveOut; + int index; + int offset; + + BasicBlock(Function *function): function(function), index(-1), offset(-1) {} + ~BasicBlock() {} + + template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(int index, uint scope = 0); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); + + Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *UNOP(AluOp op, Temp *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Temp *base, Temp *index); + Expr *MEMBER(Temp *base, const QString *name); + + Stmt *EXP(Expr *expr); + Stmt *ENTER(Expr *expr); + Stmt *LEAVE(); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); +}; + +class CloneExpr: protected V4IR::ExprVisitor +{ +public: + explicit CloneExpr(V4IR::BasicBlock *block = 0); + + void setBasicBlock(V4IR::BasicBlock *block); + + template <typename _Expr> + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template <typename _Expr> + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + V4IR::ExprList *clone(V4IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + V4IR::BasicBlock *block; + V4IR::Expr *cloned; +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4IR_P_H diff --git a/src/qml/qml/v4vm/qv4jsonobject.cpp b/src/qml/qml/v4vm/qv4jsonobject.cpp new file mode 100644 index 0000000000..cb4df70970 --- /dev/null +++ b/src/qml/qml/v4vm/qv4jsonobject.cpp @@ -0,0 +1,936 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4jsonobject.h> +#include <qv4objectproto.h> +#include <qv4numberobject.h> +#include <qv4stringobject.h> +#include <qv4booleanobject.h> +#include <qv4objectiterator.h> +#include <qjsondocument.h> +#include <qstack.h> +#include <qstringlist.h> + +#include <wtf/MathExtras.h> + +namespace QQmlJS { +namespace VM { + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + + +class Parser +{ +public: + Parser(ExecutionContext *context, const QChar *json, int length); + + Value parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + Value parseObject(); + Value parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionContext *context; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + +static const int nestingLimit = 1024; + + +Parser::Parser(ExecutionContext *context, const QChar *json, int length) + : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +QChar Parser::nextToken() +{ + if (!eatSpace()) + return 0; + QChar token = *json++; + switch (token.unicode()) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +Value Parser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug() << ">>>>> parser begin"; +#endif + + eatSpace(); + + Value v; + if (!parseValue(&v)) { +#ifdef PARSER_DEBUG + qDebug() << ">>>>> parser error"; +#endif + if (lastError == QJsonParseError::NoError) + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + // some input left... + if (eatSpace()) { + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + END; + error->offset = 0; + error->error = QJsonParseError::NoError; + return v; +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +Value Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + BEGIN << "parseObject pos=" << json; + + Object *o = context->engine->newObject(); + Value objectVal = Value::fromObject(o); + + QChar token = nextToken(); + while (token == Quote) { + if (!parseMember(o)) + return Value::undefinedValue(); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return Value::undefinedValue(); + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return Value::undefinedValue(); + } + + END; + + --nestingLevel; + return objectVal; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(Object *o) +{ + BEGIN << "parseMember"; + + QString key; + if (!parseString(&key)) + return false; + QChar token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + Value val; + if (!parseValue(&val)) + return false; + + Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); + p->value = val; + + END; + return true; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +Value Parser::parseArray() +{ + BEGIN << "parseArray"; + ArrayObject *array = context->engine->newArrayObject(context); + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return Value::undefinedValue(); + } + if (*json == EndArray) { + nextToken(); + } else { + uint index = 0; + while (1) { + Value val; + if (!parseValue(&val)) + return Value::undefinedValue(); + array->arraySet(index, val); + QChar token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return Value::undefinedValue(); + } + ++index; + } + } + + DEBUG << "size =" << array->arrayLength(); + END; + + --nestingLevel; + return Value::fromObject(array); +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(Value *val) +{ + BEGIN << "parse Value" << *json; + + switch ((json++)->unicode()) { + case 'n': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + *val = Value::nullValue(); + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + *val = Value::fromBoolean(true); + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + *val = Value::fromBoolean(false); + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + QString value; + if (!parseString(&value)) + return false; + DEBUG << "value: string"; + END; + *val = Value::fromString(context, value); + return true; + } + case BeginArray: { + *val = parseArray(); + if (val->isUndefined()) + return false; + DEBUG << "value: array"; + END; + return true; + } + case BeginObject: { + *val = parseObject(); + if (val->isUndefined()) + return false; + DEBUG << "value: object"; + END; + return true; + } + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(Value *val) +{ + BEGIN << "parseNumber" << *json; + + const QChar *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + QString number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + *val = Value::fromInt32(n); + END; + return true; + } + } + + bool ok; + double d; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + * val = Value::fromDouble(d); + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(QChar digit, uint *result) +{ + ushort d = digit.unicode(); + *result <<= 4; + if (d >= '0' && d <= '9') + *result |= (d - '0'); + else if (d >= 'a' && d <= 'f') + *result |= (d - 'a') + 10; + else if (d >= 'A' && d <= 'F') + *result |= (d - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape"; + uint escaped = (json++)->unicode(); + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + if (*ch <= 0x1f) + return false; + return true; + } + default: + return false; + } + return true; +} + + +bool Parser::parseString(QString *string) +{ + BEGIN << "parse string stringPos=" << json; + + while (json < end) { + if (*json == '"') + break; + else if (*json == '\\') { + uint ch = 0; + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + qDebug() << "scanEscape" << hex << ch; + if (QChar::requiresSurrogates(ch)) { + *string += QChar::highSurrogate(ch); + *string += QChar::lowSurrogate(ch); + } else { + *string += QChar(ch); + } + } else { + if (json->unicode() <= 0x1f) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + *string += *json; + ++json; + } + } + ++json; + + if (json > end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + END; + return true; +} + + +struct Stringify +{ + ExecutionContext *ctx; + FunctionObject *replacerFunction; + QVector<String *> propertyList; + QString gap; + QString indent; + + QStack<Object *> stack; + + Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + + QString Str(const QString &key, Value value); + QString JA(ArrayObject *a); + QString JO(Object *o); + + QString makeMember(const QString &key, Value v); +}; + +static QString quote(const QString &str) +{ + QString product = "\""; + for (int i = 0; i < str.length(); ++i) { + QChar c = str.at(i); + switch (c.unicode()) { + case '"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + if (c.unicode() <= 0x1f) { + product += "\\u00"; + product += c.unicode() > 0xf ? '1' : '0'; + product += "0123456789abcdef"[c.unicode() & 0xf]; + } else { + product += c; + } + } + } + product += '"'; + return product; +} + +QString Stringify::Str(const QString &key, Value value) +{ + QString result; + + if (Object *o = value.asObject()) { + FunctionObject *toJSON = o->get(ctx, ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject(); + if (toJSON) { + Value arg = Value::fromString(ctx, key); + value = toJSON->call(ctx, value, &arg, 1); + } + } + + if (replacerFunction) { + Object *holder = ctx->engine->newObject(); + Value holderValue = Value::fromObject(holder); + holder->put(ctx, QString(), value); + Value args[2]; + args[0] = Value::fromString(ctx, key); + args[1] = value; + value = replacerFunction->call(ctx, holderValue, args, 2); + } + + if (Object *o = value.asObject()) { + if (NumberObject *n = o->asNumberObject()) + value = n->value; + else if (StringObject *so = o->asStringObject()) + value = so->value; + else if (BooleanObject *b =o->asBooleanObject()) + value = b->value; + } + + if (value.isNull()) + return QStringLiteral("null"); + if (value.isBoolean()) + return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value.isString()) + return quote(value.stringValue()->toQString()); + + if (value.isNumber()) { + double d = value.toNumber(); + return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + } + + if (Object *o = value.asObject()) { + if (!o->asFunctionObject()) { + if (o->asArrayObject()) + return JA(static_cast<ArrayObject *>(o)); + else + return JO(o); + } + } + + return QString(); +} + +QString Stringify::makeMember(const QString &key, Value v) +{ + QString strP = Str(key, v); + if (!strP.isEmpty()) { + QString member = quote(key) + ':'; + if (!gap.isEmpty()) + member += ' '; + member += strP; + return member; + } + return QString(); +} + +QString Stringify::JO(Object *o) +{ + if (stack.contains(o)) + ctx->throwTypeError(); + + QString result; + stack.push(o); + QString stepback = indent; + indent += gap; + + QStringList partial; + if (propertyList.isEmpty()) { + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + + while (1) { + String *name; + uint index; + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); + if (!pd) + break; + Value v = o->getValue(ctx, pd, attrs); + QString key; + if (name) + key = name->toQString(); + else + key = QString::number(index); + QString member = makeMember(key, v); + if (!member.isEmpty()) + partial += member; + } + } else { + for (int i = 0; i < propertyList.size(); ++i) { + bool exists; + Value v = o->get(ctx, propertyList.at(i), &exists); + if (!exists) + continue; + QString member = makeMember(propertyList.at(i)->toQString(), v); + if (!member.isEmpty()) + partial += member; + } + } + + if (partial.isEmpty()) { + result = QStringLiteral("{}"); + } else if (gap.isEmpty()) { + result = "{" + partial.join(",") + "}"; + } else { + QString separator = ",\n" + indent; + result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; + } + + indent = stepback; + stack.pop(); + return result; +} + +QString Stringify::JA(ArrayObject *a) +{ + if (stack.contains(a)) + ctx->throwTypeError(); + + QString result; + stack.push(a); + QString stepback = indent; + indent += gap; + + QStringList partial; + uint len = a->arrayLength(); + for (uint i = 0; i < len; ++i) { + bool exists; + Value v = a->getIndexed(ctx, i, &exists); + if (!exists) { + partial += QStringLiteral("null"); + continue; + } + QString strP = Str(QString::number(i), v); + if (!strP.isEmpty()) + partial += strP; + else + partial += QStringLiteral("null"); + } + + if (partial.isEmpty()) { + result = QStringLiteral("[]"); + } else if (gap.isEmpty()) { + result = "[" + partial.join(",") + "]"; + } else { + QString separator = ",\n" + indent; + result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; + } + + indent = stepback; + stack.pop(); + return result; +} + + +JsonObject::JsonObject(ExecutionContext *context) + : Object(context->engine) +{ + type = Type_JSONObject; + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); +} + + +Value JsonObject::method_parse(SimpleCallContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + DEBUG << "parsing source = " << jtext; + Parser parser(ctx, jtext.constData(), jtext.length()); + QJsonParseError error; + Value result = parser.parse(&error); + if (error.error != QJsonParseError::NoError) { + DEBUG << "parse error" << error.errorString(); + ctx->throwSyntaxError(0); + } + + return result; +} + +Value JsonObject::method_stringify(SimpleCallContext *ctx) +{ + Stringify stringify(ctx); + + Object *o = ctx->argument(1).asObject(); + if (o) { + stringify.replacerFunction = o->asFunctionObject(); + if (o->isArrayObject()) { + uint arrayLen = o->arrayLength(); + for (uint i = 0; i < arrayLen; ++i) { + Value v = o->getIndexed(ctx, i); + if (v.asNumberObject() || v.asStringObject() || v.isNumber()) + v = __qmljs_to_string(v, ctx); + if (v.isString()) { + String *s = v.stringValue(); + if (!stringify.propertyList.contains(s)) + stringify.propertyList.append(s); + } + } + } + } + + Value s = ctx->argument(2); + if (NumberObject *n = s.asNumberObject()) + s = n->value; + else if (StringObject *so = s.asStringObject()) + s = so->value; + + if (s.isNumber()) { + stringify.gap = QString(qMin(10, (int)s.toInteger()), ' '); + } else if (s.isString()) { + stringify.gap = s.stringValue()->toQString().left(10); + } + + + QString result = stringify.Str(QString(), ctx->argument(0)); + if (result.isEmpty()) + return Value::undefinedValue(); + return Value::fromString(ctx, result); +} + + + +} +} diff --git a/src/qml/qml/v4vm/qv4jsonobject.h b/src/qml/qml/v4vm/qv4jsonobject.h new file mode 100644 index 0000000000..dba4786c2b --- /dev/null +++ b/src/qml/qml/v4vm/qv4jsonobject.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSONOBJECTS_H +#define QV4SJONOBJECTS_H + +#include <qv4object.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct JsonObject : Object { + JsonObject(ExecutionContext *context); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_stringify(SimpleCallContext *ctx); + +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml/v4vm/qv4lookup.cpp b/src/qml/qml/v4vm/qv4lookup.cpp new file mode 100644 index 0000000000..38a11a99de --- /dev/null +++ b/src/qml/qml/v4vm/qv4lookup.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4lookup.h" +#include "qv4functionobject.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +void Lookup::lookupPropertyGeneric(QQmlJS::VM::Lookup *l, ExecutionContext *ctx, QQmlJS::VM::Value *result, const QQmlJS::VM::Value &object) +{ + if (Object *o = object.asObject()) { + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->lookupProperty = lookupProperty0; + else if (l->level == 1) + l->lookupProperty = lookupProperty1; + else if (l->level == 2) + l->lookupProperty = lookupProperty2; + if (result) + *result = p->value; + return; + } else { + if (l->level == 0) + l->lookupProperty = lookupPropertyAccessor0; + else if (l->level == 1) + l->lookupProperty = lookupPropertyAccessor1; + else if (l->level == 2) + l->lookupProperty = lookupPropertyAccessor2; + if (result) + *result = p->value; + Value res = o->getValue(ctx, p, attrs); + if (result) + *result = res; + return; + } + } else if (result) { + *result = Value::undefinedValue(); + } + } else { + Value res; + if (Managed *m = object.asManaged()) { + res = m->get(ctx, l->name); + } else { + o = __qmljs_convert_to_object(ctx, object); + res = o->get(ctx, l->name); + } + if (result) + *result = res; + } +} + +void Lookup::lookupProperty0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + +void Lookup::lookupProperty1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + if (result) + *result = o->prototype->memberData[l->index].value; + return; + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + +void Lookup::lookupProperty2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + +void Lookup::lookupPropertyAccessor0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(ctx, object, 0, 0); + if (result) + *result = res; + return; + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + +void Lookup::lookupPropertyAccessor1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + Value res; + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(ctx, object, 0, 0); + if (result) + *result = res; + return; + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + +void Lookup::lookupPropertyAccessor2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(ctx, object, 0, 0); + if (result) + *result = res; + return; + } + } + } + } + l->lookupProperty = lookupPropertyGeneric; + lookupPropertyGeneric(l, ctx, result, object); +} + + +void Lookup::lookupGlobalGeneric(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->lookupGlobal = lookupGlobal0; + else if (l->level == 1) + l->lookupGlobal = lookupGlobal1; + else if (l->level == 2) + l->lookupGlobal = lookupGlobal2; + *result = p->value; + return; + } else { + if (l->level == 0) + l->lookupGlobal = lookupGlobalAccessor0; + else if (l->level == 1) + l->lookupGlobal = lookupGlobalAccessor1; + else if (l->level == 2) + l->lookupGlobal = lookupGlobalAccessor2; + Value res = o->getValue(ctx, p, attrs); + if (result) + *result = res; + return; + } + } + ctx->throwReferenceError(Value::fromString(l->name)); +} + +void Lookup::lookupGlobal0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + *result = o->memberData[l->index].value; + return; + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +void Lookup::lookupGlobal1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +void Lookup::lookupGlobal2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + } + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +void Lookup::lookupGlobalAccessor0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(ctx, Value::undefinedValue(), 0, 0); + return; + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +void Lookup::lookupGlobalAccessor1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(ctx, Value::undefinedValue(), 0, 0); + return; + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +void Lookup::lookupGlobalAccessor2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(ctx, Value::undefinedValue(), 0, 0); + return; + } + } + } + l->lookupGlobal = lookupGlobalGeneric; + lookupGlobalGeneric(l, ctx, result); +} + +} +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4vm/qv4lookup.h b/src/qml/qml/v4vm/qv4lookup.h new file mode 100644 index 0000000000..38546cd65b --- /dev/null +++ b/src/qml/qml/v4vm/qv4lookup.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4LOOKUP_H +#define QV4LOOKUP_H + +#include "qv4global.h" +#include "qv4runtime.h" +#include "qv4engine.h" +#include "qv4context.h" +#include "qv4object.h" +#include "qv4internalclass.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Lookup { + enum { Size = 3 }; + union { + void (*lookupProperty)(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + void (*lookupGlobal)(Lookup *l, ExecutionContext *ctx, Value *result); + }; + InternalClass *classList[Size]; + int level; + uint index; + String *name; + + static void lookupPropertyGeneric(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupProperty0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupProperty1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupProperty2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupPropertyAccessor0(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupPropertyAccessor1(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + static void lookupPropertyAccessor2(Lookup *l, ExecutionContext *ctx, Value *result, const Value &object); + + static void lookupGlobalGeneric(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobal0(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobal1(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobal2(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobalAccessor0(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobalAccessor1(Lookup *l, ExecutionContext *ctx, Value *result); + static void lookupGlobalAccessor2(Lookup *l, ExecutionContext *ctx, Value *result); + + Property *lookup(Object *obj, PropertyAttributes *attrs) { + int i = 0; + while (i < level && obj && obj->internalClass == classList[i]) { + obj = obj->prototype; + ++i; + } + + if (index != UINT_MAX && obj->internalClass == classList[i]) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + while (i < Size && obj) { + classList[i] = obj->internalClass; + + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + level = i; + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + ++i; + } + level = i; + + while (obj) { + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + } + return 0; + } + + Property *setterLookup(Object *o, PropertyAttributes *attrs) { + if (o->internalClass == classList[0]) { + *attrs = o->internalClass->propertyData[index]; + return o->memberData + index; + } + + uint idx = o->internalClass->find(name); + if (idx != UINT_MAX) { + classList[0] = o->internalClass; + index = idx; + *attrs = o->internalClass->propertyData[index]; + return o->memberData + index; + } + return 0; + } +}; + +} +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4managed.cpp b/src/qml/qml/v4vm/qv4managed.cpp new file mode 100644 index 0000000000..ab87b9dd9f --- /dev/null +++ b/src/qml/qml/v4vm/qv4managed.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4managed.h" +#include "qv4mm.h" +#include "qv4errorobject.h" + +using namespace QQmlJS::VM; + +const ManagedVTable Managed::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + hasInstance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + "Managed", +}; + + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = static_cast<Managed *>(ptr); + m->vtbl = 0; + m->_data = 0; + m->~Managed(); +} + +void Managed::operator delete(void *ptr, MemoryManager *mm) +{ + Q_UNUSED(mm); + + operator delete(ptr); +} + +QString Managed::className() const +{ + const char *s = 0; + switch (Type(type)) { + case Type_Invalid: + case Type_String: + return QString(); + case Type_Object: + s = "Object"; + break; + case Type_ArrayObject: + s = "Array"; + break; + case Type_FunctionObject: + s = "Function"; + break; + case Type_BooleanObject: + s = "Boolean"; + break; + case Type_NumberObject: + s = "Number"; + break; + case Type_StringObject: + s = "String"; + break; + case Type_DateObject: + s = "Date"; + break; + case Type_RegExpObject: + s = "RegExp"; + break; + case Type_ErrorObject: + switch (ErrorObject::ErrorType(subtype)) { + case ErrorObject::Error: + s = "Error"; + break; + case ErrorObject::EvalError: + s = "EvalError"; + break; + case ErrorObject::RangeError: + s = "RangeError"; + break; + case ErrorObject::ReferenceError: + s = "ReferenceError"; + break; + case ErrorObject::SyntaxError: + s = "SyntaxError"; + break; + case ErrorObject::TypeError: + s = "TypeError"; + break; + case ErrorObject::URIError: + s = "URIError"; + break; + } + break; + case Type_ArgumentsObject: + s = "Arguments"; + break; + case Type_JSONObject: + s = "JSON"; + break; + case Type_MathObject: + s = "Math"; + break; + case Type_ForeachIteratorObject: + s = "__ForeachIterator"; + break; + } + return QString::fromLatin1(s); +} + +bool Managed::hasInstance(Managed *, ExecutionContext *ctx, const Value &) +{ + ctx->throwTypeError(); +} + +Value Managed::construct(Managed *, ExecutionContext *context, Value *, int) +{ + context->throwTypeError(); +} + +Value Managed::call(Managed *, ExecutionContext *context, const Value &, Value *, int) +{ + context->throwTypeError(); +} + +Value Managed::get(ExecutionContext *ctx, String *name, bool *hasProperty) +{ + return vtbl->get(this, ctx, name, hasProperty); +} + +Value Managed::getIndexed(ExecutionContext *ctx, uint index, bool *hasProperty) +{ + return vtbl->getIndexed(this, ctx, index, hasProperty); +} diff --git a/src/qml/qml/v4vm/qv4managed.h b/src/qml/qml/v4vm/qv4managed.h new file mode 100644 index 0000000000..844c88772b --- /dev/null +++ b/src/qml/qml/v4vm/qv4managed.h @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MANAGED_H +#define QMLJS_MANAGED_H + +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QDebug> +#include "qv4global.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +class MemoryManager; +struct String; +struct Object; +struct ObjectPrototype; +struct ExecutionContext; +struct ScriptFunction; + +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct JSONObject; +struct ForeachIteratorObject; +struct Managed; +struct Value; +struct RegExp; + +struct ManagedVTable +{ + Value (*call)(Managed *, ExecutionContext *context, const Value &thisObject, Value *args, int argc); + Value (*construct)(Managed *, ExecutionContext *context, Value *args, int argc); + void (*markObjects)(Managed *); + void (*destroy)(Managed *); + bool (*hasInstance)(Managed *, ExecutionContext *ctx, const Value &value); + Value (*get)(Managed *, ExecutionContext *ctx, String *name, bool *hasProperty); + Value (*getIndexed)(Managed *, ExecutionContext *ctx, uint index, bool *hasProperty); + void (*put)(Managed *, ExecutionContext *ctx, String *name, const Value &value); + void (*putIndexed)(Managed *, ExecutionContext *ctx, uint index, const Value &value); + PropertyAttributes (*query)(Managed *, ExecutionContext *ctx, String *name); + PropertyAttributes (*queryIndexed)(Managed *, ExecutionContext *ctx, uint index); + bool (*deleteProperty)(Managed *m, ExecutionContext *ctx, String *name); + bool (*deleteIndexedProperty)(Managed *m, ExecutionContext *ctx, uint index); + const char *className; +}; + +#define DEFINE_MANAGED_VTABLE(classname) \ +const ManagedVTable classname::static_vtbl = \ +{ \ + call, \ + construct, \ + markObjects, \ + destroy, \ + hasInstance, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + #classname \ +} + + +struct Q_V4_EXPORT Managed +{ +private: + void *operator new(size_t); + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed() + : vtbl(&static_vtbl), _data(0) + { inUse = 1; extensible = 1; } + +public: + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + void operator delete(void *ptr, MemoryManager *mm); + + inline void mark() { + if (markBit) + return; + markBit = 1; + if (vtbl->markObjects) + vtbl->markObjects(this); + } + + enum Type { + Type_Invalid, + Type_String, + Type_Object, + Type_ArrayObject, + Type_FunctionObject, + Type_BooleanObject, + Type_NumberObject, + Type_StringObject, + Type_DateObject, + Type_RegExpObject, + Type_ErrorObject, + Type_ArgumentsObject, + Type_JSONObject, + Type_MathObject, + Type_ForeachIteratorObject, + Type_RegExp + }; + + String *asString() { return reinterpret_cast<String *>(this); } + Object *asObject() { return reinterpret_cast<Object *>(this); } + ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } + FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } + BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } + NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } + StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } + DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } + RegExpObject *asRegExpObject() { return type == Type_RegExpObject ? reinterpret_cast<RegExpObject *>(this) : 0; } + ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } + ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } + JSONObject *asJSONObject() { return type == Type_JSONObject ? reinterpret_cast<JSONObject *>(this) : 0; } + ForeachIteratorObject *asForeachIteratorObject() { return type == Type_ForeachIteratorObject ? reinterpret_cast<ForeachIteratorObject *>(this) : 0; } + RegExp *asRegExp() { return type == Type_RegExp ? reinterpret_cast<RegExp *>(this) : 0; } + + bool isArrayObject() const { return type == Type_ArrayObject; } + bool isStringObject() const { return type == Type_StringObject; } + + QString className() const; + + Managed **nextFreeRef() { + return reinterpret_cast<Managed **>(this); + } + Managed *nextFree() { + return *reinterpret_cast<Managed **>(this); + } + void setNextFree(Managed *m) { + *reinterpret_cast<Managed **>(this) = m; + } + + inline bool hasInstance(ExecutionContext *ctx, const Value &v) { + return vtbl->hasInstance(this, ctx, v); + } + Value construct(ExecutionContext *context, Value *args, int argc); + Value call(ExecutionContext *context, const Value &thisObject, Value *args, int argc); + Value get(ExecutionContext *ctx, String *name, bool *hasProperty = 0); + Value getIndexed(ExecutionContext *ctx, uint index, bool *hasProperty = 0); + void put(ExecutionContext *ctx, String *name, const Value &value) + { vtbl->put(this, ctx, name, value); } + void putIndexed(ExecutionContext *ctx, uint index, const Value &value) + { vtbl->putIndexed(this, ctx, index, value); } + bool deleteProperty(ExecutionContext *ctx, String *name) + { return vtbl->deleteProperty(this, ctx, name); } + bool deleteIndexedProperty(ExecutionContext *ctx, uint index) + { return vtbl->deleteIndexedProperty(this, ctx, index); } + + static void destroy(Managed *that) { that->_data = 0; } + static bool hasInstance(Managed *that, ExecutionContext *ctx, const Value &value); + static Value construct(Managed *, ExecutionContext *context, Value *, int); + static Value call(Managed *, ExecutionContext *, const Value &, Value *, int); + + uint internalType() const { + return type; + } + +protected: + + static const ManagedVTable static_vtbl; + + const ManagedVTable *vtbl; + + union { + uint _data; + struct { + uint markBit : 1; + uint inUse : 1; + uint extensible : 1; // used by Object + uint isNonStrictArgumentsObject : 1; + uint isBuiltinFunction : 1; // used by FunctionObject + uint needsActivation : 1; // used by FunctionObject + uint usesArgumentsObject : 1; // used by FunctionObject + uint strictMode : 1; // used by FunctionObject + uint type : 5; + mutable uint subtype : 3; + uint externalComparison : 1; + uint unused : 15; + }; + }; + +private: + friend class MemoryManager; + friend struct Identifiers; +}; + +} +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4math.h b/src/qml/qml/v4vm/qv4math.h new file mode 100644 index 0000000000..0699c0c971 --- /dev/null +++ b/src/qml/qml/v4vm/qv4math.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MATH_H +#define QMLJS_MATH_H + +#ifndef QMLJS_LLVM_RUNTIME +# include <QtCore/qnumeric.h> +#endif // QMLJS_LLVM_RUNTIME +#include <cmath> + +#if !defined(QMLJS_LLVM_RUNTIME) && COMPILER(GCC) && (CPU(X86_64) || CPU(X86)) +#define QMLJS_INLINE_MATH +#define QMLJS_READONLY __attribute((const)) +#endif + +#if defined(QMLJS_INLINE_MATH) + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +static inline QMLJS_READONLY Value add_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a + (double)b); +} + +static inline QMLJS_READONLY Value sub_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a - (double)b); +} + +static inline QMLJS_READONLY Value mul_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("imul %2, %1\n" + "setc %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a * (double)b); +} + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // defined(QMLJS_INLINE_MATH) + +#ifdef QMLJS_READONLY +#undef QMLJS_READONLY +#endif + +#endif // QMLJS_MATH_H diff --git a/src/qml/qml/v4vm/qv4mathobject.cpp b/src/qml/qml/v4vm/qv4mathobject.cpp new file mode 100644 index 0000000000..d1017ebad6 --- /dev/null +++ b/src/qml/qml/v4vm/qv4mathobject.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4mathobject.h" +#include "qv4objectproto.h" + +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> + +using namespace QQmlJS::VM; + +static const double qt_PI = 2.0 * ::asin(1.0); + +MathObject::MathObject(ExecutionContext *ctx) + : Object(ctx->engine) +{ + type = Type_MathObject; + prototype = ctx->engine->objectPrototype; + + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +Value MathObject::method_abs(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromDouble(qSNaN()); + + if (context->arguments[0].isInteger()) { + int i = context->arguments[0].integerValue(); + return Value::fromInt32(i < 0 ? - i : i); + } + + double v = context->arguments[0].toNumber(); + if (v == 0) // 0 | -0 + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); +} + +Value MathObject::method_acos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); +} + +Value MathObject::method_asin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::asin(v)); +} + +Value MathObject::method_atan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::atan(v)); +} + +Value MathObject::method_atan2(SimpleCallContext *context) +{ + double v1 = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + double v2 = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) + return Value::fromDouble(copySign(0, -1.0)); + + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(qt_PI); + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(-qt_PI); + } + } + return Value::fromDouble(::atan2(v1, v2)); +} + +Value MathObject::method_ceil(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0.0 && v > -1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(::ceil(v)); +} + +Value MathObject::method_cos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::cos(v)); +} + +Value MathObject::method_exp(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + return Value::fromDouble(0); + else + return Value::fromDouble(qInf()); + } else { + return Value::fromDouble(::exp(v)); + } +} + +Value MathObject::method_floor(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::floor(v)); +} + +Value MathObject::method_log(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::log(v)); +} + +Value MathObject::method_max(SimpleCallContext *context) +{ + double mx = -qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if (x > mx || isnan(x)) + mx = x; + } + return Value::fromDouble(mx); +} + +Value MathObject::method_min(SimpleCallContext *context) +{ + double mx = qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || isnan(x)) { + mx = x; + } + } + return Value::fromDouble(mx); +} + +Value MathObject::method_pow(SimpleCallContext *context) +{ + double x = context->argumentCount > 0 ? context->arguments[0].toNumber() : qSNaN(); + double y = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if (isnan(y)) + return Value::fromDouble(qSNaN()); + + if (y == 0) { + return Value::fromDouble(1); + } else if (((x == 1) || (x == -1)) && isinf(y)) { + return Value::fromDouble(qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + return Value::fromDouble(qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } +#endif + else { + return Value::fromDouble(::pow(x, y)); + } + // ### + return Value::fromDouble(qSNaN()); +} + +Value MathObject::method_random(SimpleCallContext *) +{ + return Value::fromDouble(qrand() / (double) RAND_MAX); +} + +Value MathObject::method_round(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + v = copySign(::floor(v + 0.5), v); + return Value::fromDouble(v); +} + +Value MathObject::method_sin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sin(v)); +} + +Value MathObject::method_sqrt(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sqrt(v)); +} + +Value MathObject::method_tan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::tan(v)); +} + diff --git a/src/qml/qml/v4vm/qv4mathobject.h b/src/qml/qml/v4vm/qv4mathobject.h new file mode 100644 index 0000000000..68ca87d38b --- /dev/null +++ b/src/qml/qml/v4vm/qv4mathobject.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MATHOBJECT_H +#define QV$MATHOBJECT_H + +#include "qv4object.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct MathObject: Object +{ + MathObject(ExecutionContext *ctx); + + static Value method_abs(SimpleCallContext *context); + static Value method_acos(SimpleCallContext *context); + static Value method_asin(SimpleCallContext *context); + static Value method_atan(SimpleCallContext *context); + static Value method_atan2(SimpleCallContext *context); + static Value method_ceil(SimpleCallContext *context); + static Value method_cos(SimpleCallContext *context); + static Value method_exp(SimpleCallContext *context); + static Value method_floor(SimpleCallContext *context); + static Value method_log(SimpleCallContext *context); + static Value method_max(SimpleCallContext *context); + static Value method_min(SimpleCallContext *context); + static Value method_pow(SimpleCallContext *context); + static Value method_random(SimpleCallContext *context); + static Value method_round(SimpleCallContext *context); + static Value method_sin(SimpleCallContext *context); + static Value method_sqrt(SimpleCallContext *context); + static Value method_tan(SimpleCallContext *context); +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4vm/qv4mm.cpp b/src/qml/qml/v4vm/qv4mm.cpp new file mode 100644 index 0000000000..19e0e3fd68 --- /dev/null +++ b/src/qml/qml/v4vm/qv4mm.cpp @@ -0,0 +1,493 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4engine.h" +#include "qv4object.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include "PageAllocation.h" +#include "StdLibExtras.h" + +#include <QTime> +#include <QVector> +#include <QVector> +#include <QMap> + +#include <iostream> +#include <cstdlib> +#include "qv4alloca_p.h" + +#ifdef V4_USE_VALGRIND +#include <valgrind/valgrind.h> +#include <valgrind/memcheck.h> +#endif + +using namespace QQmlJS::VM; +using namespace WTF; + +static const std::size_t CHUNK_SIZE = 1024*32; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + quintptr *stackTop; + + enum { MaxItemSize = 256 }; + Managed *smallItems[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + struct Chunk { + PageAllocation memory; + int chunkSize; + }; + + QVector<Chunk> heapChunks; + QHash<Managed *, uint> protectedObject; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector<unsigned> allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + , stackTop(0) + { + memset(smallItems, 0, sizeof(smallItems)); + memset(nChunks, 0, sizeof(nChunks)); + memset(availableItems, 0, sizeof(availableItems)); + memset(allocCount, 0, sizeof(allocCount)); + scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QVector<Chunk>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->memory.deallocate(); + } +}; + +#define SCRIBBLE(obj, c, size) \ + if (m_d->scribble) \ + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); + + +namespace QQmlJS { namespace VM { + +bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +{ + return a.memory.base() < b.memory.base(); +} + +} } // namespace QQmlJS::VM + +MemoryManager::MemoryManager() + : m_d(new Data(true)) + , m_contextList(0) +{ + setEnableGC(true); +#ifdef V4_USE_VALGRIND + VALGRIND_CREATE_MEMPOOL(this, 0, true); +#endif + +#if USE(PTHREADS) +# if OS(DARWIN) + void *st = pthread_get_stackaddr_np(pthread_self()); + m_d->stackTop = static_cast<quintptr *>(st); +# else + void* stackBottom = 0; + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + + m_d->stackTop = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr); +# endif +#elif OS(WINDOWS) +# if COMPILER(MSVC) + PNT_TIB tib = (PNT_TIB)NtCurrentTeb(); + m_d->stackTop = static_cast<quintptr*>(tib->StackBase); +# else +# error "Unsupported compiler: no way to get the top-of-stack." +# endif +#else +# error "Unsupported platform: no way to get the top-of-stack." +#endif + +} + +Managed *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + assert(size >= 16); + assert(size % 16 == 0); + + size_t pos = size >> 4; + ++m_d->allocCount[pos]; + + // fits into a small bucket + assert(size < MemoryManager::Data::MaxItemSize); + + Managed *m = m_d->smallItems[pos]; + if (m) + goto found; + + // try to free up space, otherwise allocate + if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) { + runGC(); + m = m_d->smallItems[pos]; + if (m) + goto found; + } + + // no free item available, allocate a new chunk + { + // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks + uint shift = ++m_d->nChunks[pos]; + if (shift > 10) + shift = 10; + std::size_t allocSize = CHUNK_SIZE*(1 << shift)*size; + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + Data::Chunk allocation; + allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.chunkSize = size; + m_d->heapChunks.append(allocation); + qSort(m_d->heapChunks); + char *chunk = (char *)allocation.memory.base(); + char *end = chunk + allocation.memory.size() - size; + memset(chunk, 0, allocation.memory.size()); + Managed **last = &m_d->smallItems[pos]; + while (chunk <= end) { + Managed *o = reinterpret_cast<Managed *>(chunk); + o->_data = 0; + *last = o; + last = o->nextFreeRef(); + chunk += size; + } + *last = 0; + m = m_d->smallItems[pos]; + m_d->availableItems[pos] += allocation.memory.size()/size - 1; +#ifdef V4_USE_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize); +#endif + } + + found: +#ifdef V4_USE_VALGRIND + VALGRIND_MEMPOOL_ALLOC(this, m, size); +#endif + + m_d->smallItems[pos] = m->nextFree(); + return m; +} + +void MemoryManager::mark() +{ + m_d->engine->markObjects(); + + for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + it.key()->mark(); + + // push all caller saved registers to the stack, so we can find the objects living in these registers +#if COMPILER(MSVC) +# if CPU(X86_64) + HANDLE thread = GetCurrentThread(); + WOW64_CONTEXT ctxt; + /*bool success =*/ Wow64GetThreadContext(thread, &ctxt); +# elif CPU(X86) + HANDLE thread = GetCurrentThread(); + CONTEXT ctxt; + /*bool success =*/ GetThreadContext(thread, &ctxt); +# endif // CPU +#elif COMPILER(CLANG) || COMPILER(GCC) +# if CPU(X86_64) + quintptr regs[5]; + asm( + "mov %%rbp, %0\n" + "mov %%r12, %1\n" + "mov %%r13, %2\n" + "mov %%r14, %3\n" + "mov %%r15, %4\n" + : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4]) + : + : + ); +# endif // CPU +#endif // COMPILER + + collectFromStack(); +} + +std::size_t MemoryManager::sweep() +{ + std::size_t freedCount = 0; + + for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize); + + ExecutionContext *ctx = m_contextList; + ExecutionContext **n = &m_contextList; + while (ctx) { + ExecutionContext *next = ctx->next; + if (!ctx->marked) { + free(ctx); + *n = next; + } else { + ctx->marked = false; + n = &ctx->next; + } + ctx = next; + } + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) +{ +// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); + std::size_t freedCount = 0; + + Managed **f = &m_d->smallItems[size >> 4]; + +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) { + Managed *m = reinterpret_cast<Managed *>(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + + if (m->inUse) { + if (m->markBit) { + m->markBit = 0; + } else { +// qDebug() << "-- collecting it." << m << *f << m->nextFree(); +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + m->vtbl->destroy(m); + + m->setNextFree(*f); +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; + VALGRIND_MEMPOOL_FREE(this, m); +#endif + *f = m; + SCRIBBLE(m, 0x99, size); + ++freedCount; + } + } + } +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +void MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return; + } + +// QTime t; t.start(); + +// qDebug() << ">>>>>>>>runGC"; + + mark(); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t freedCount =*/ sweep(); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + sweep(); +} + +void MemoryManager::protect(Managed *m) +{ + ++m_d->protectedObject[m]; +} + +void MemoryManager::unprotect(Managed *m) +{ + if (!--m_d->protectedObject[m]) + m_d->protectedObject.remove(m); +} + +static inline void add(QVector<Managed *> &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::dumpStats() const +{ +#ifdef DETAILED_MM_STATS + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector<unsigned> &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} + +#endif // DETAILED_MM_STATS + +void MemoryManager::collectFromStack() const +{ + quintptr valueOnStack = 0; + + if (!m_d->heapChunks.count()) + return; + + quintptr *current = (&valueOnStack) + 1; +// qDebug() << "collectFromStack";// << top << current << &valueOnStack; + +#if V4_USE_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(current, (m_d->stackTop - current)*sizeof(quintptr)); +#endif + + char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); + char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); + int i = 0; + for (QVector<Data::Chunk>::Iterator it = m_d->heapChunks.begin(), end = + m_d->heapChunks.end(); it != end; ++it) { + heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) - 1; + heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) + it->memory.size() - it->chunkSize; + } + assert(i == m_d->heapChunks.count() * 2); + + for (; current < m_d->stackTop; ++current) { + char* genericPtr = +#if QT_POINTER_SIZE == 8 + reinterpret_cast<char *>((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); +#else + reinterpret_cast<char *>(*current); +#endif + + if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) + continue; + int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; + // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + assert(index >= 0 && index < m_d->heapChunks.count() * 2); + if (index & 1) { + int size = m_d->heapChunks.at(index >> 1).chunkSize; + Managed *m = reinterpret_cast<Managed *>(genericPtr); +// qDebug() << " inside" << size; + + if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1] - 1 ) % size) + // wrongly aligned value, skip it + continue; + + if (!m->inUse) + // Skip pointers to already freed objects, they are bogus as well + continue; + +// qDebug() << " marking"; + m->mark(); + } + } +} diff --git a/src/qml/qml/v4vm/qv4mm.h b/src/qml/qml/v4vm/qv4mm.h new file mode 100644 index 0000000000..a41df835c3 --- /dev/null +++ b/src/qml/qml/v4vm/qv4mm.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qv4global.h" +#include "qv4context.h" + +#include <QScopedPointer> + +//#define DETAILED_MM_STATS + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct ExecutionEngine; +struct ExecutionContext; +struct Managed; + +class Q_V4_EXPORT MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + +public: + struct Data; + + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + ~MemoryManager(); + + void protect(Managed *m); + void unprotect(Managed *m); + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + Managed *o = alloc(size); + return o; + } + + ExecutionContext *allocContext(uint size); + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + void runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + + void dumpStats() const; + +protected: + /// expects size to be aligned + // TODO: try to inline + Managed *alloc(std::size_t size); + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectFromStack() const; + void mark(); + std::size_t sweep(); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size); + +protected: + QScopedPointer<Data> m_d; + ExecutionContext *m_contextList; +}; + +inline ExecutionContext *MemoryManager::allocContext(uint size) +{ + ExecutionContext *newContext = (ExecutionContext *)malloc(size); + newContext->next = m_contextList; + m_contextList = newContext; + return newContext; +} + + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4GC_H diff --git a/src/qml/qml/v4vm/qv4numberobject.cpp b/src/qml/qml/v4vm/qv4numberobject.cpp new file mode 100644 index 0000000000..f32c8b4f97 --- /dev/null +++ b/src/qml/qml/v4vm/qv4numberobject.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4numberobject.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> + + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(NumberCtor); + +NumberCtor::NumberCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value NumberCtor::construct(Managed *, ExecutionContext *ctx, Value *args, int argc) +{ + double d = argc ? args[0].toNumber() : 0.; + return Value::fromObject(ctx->engine->newNumberObject(Value::fromDouble(d))); +} + +Value NumberCtor::call(Managed *m, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) +{ + double d = argc ? argv[0].toNumber() : 0.; + return Value::fromDouble(d); +} + +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); +} + +Value NumberPrototype::method_toString(SimpleCallContext *ctx) +{ + double num; + if (ctx->thisObject.isNumber()) { + num = ctx->thisObject.asDouble(); + } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + num = thisObject->value.asDouble(); + } + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } + + if (isnan(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); + } + } + + String *str = Value::fromDouble(num).toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_valueOf(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} + +Value NumberPrototype::method_toFixed(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(); + + if (isnan(fdigits)) + fdigits = 0; + + if (fdigits < 0 || fdigits > 20) + ctx->throwRangeError(ctx->thisObject); + + double v = thisObject->value.asDouble(); + QString str; + if (isnan(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else if (v < 1.e21) + str = QString::number(v, 'f', int (fdigits)); + else + return __qmljs_string_from_number(ctx, v); + return Value::fromString(ctx, str); +} + +Value NumberPrototype::method_toExponential(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(); + + QString z = QString::number(thisObject->value.asDouble(), 'e', int (fdigits)); + return Value::fromString(ctx, z); +} + +Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(); + + return Value::fromString(ctx, QString::number(thisObject->value.asDouble(), 'g', int (fdigits))); +} diff --git a/src/qml/qml/v4vm/qv4numberobject.h b/src/qml/qml/v4vm/qv4numberobject.h new file mode 100644 index 0000000000..d8be4790da --- /dev/null +++ b/src/qml/qml/v4vm/qv4numberobject.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4NUMBEROBJECT_H +#define QV4NUMBEROBJECT_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct NumberCtor: FunctionObject +{ + NumberCtor(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct NumberPrototype: NumberObject +{ + NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_toFixed(SimpleCallContext *ctx); + static Value method_toExponential(SimpleCallContext *ctx); + static Value method_toPrecision(SimpleCallContext *ctx); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4object.cpp b/src/qml/qml/v4vm/qv4object.cpp new file mode 100644 index 0000000000..5091ceb095 --- /dev/null +++ b/src/qml/qml/v4vm/qv4object.cpp @@ -0,0 +1,1177 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4argumentsobject.h" +#include "qv4mm.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include "qv4alloca_p.h" + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(Object); + +Object::Object(ExecutionEngine *engine) + : prototype(0) + , internalClass(engine->emptyClass) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) + , externalResource(0) +{ + vtbl = &static_vtbl; + type = Type_Object; +} + +Object::Object(ExecutionContext *context) + : prototype(0) + , internalClass(context->engine->emptyClass) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) + , externalResource(0) +{ + vtbl = &static_vtbl; + type = Type_Object; +} + +Object::~Object() +{ + delete externalResource; + if (memberData != inlineProperties) + delete [] memberData; + delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); + if (arrayAttributes) + delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); + delete sparseArray; + _data = 0; +} + +void Object::destroy(Managed *that) +{ + static_cast<Object *>(that)->~Object(); +} + +void Object::put(ExecutionContext *ctx, const QString &name, const Value &value) +{ + put(ctx, ctx->engine->newString(name), value); +} + +Value Object::getValue(const Value &thisObject, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) +{ + if (!attrs.isAccessor()) + return p->value; + FunctionObject *getter = p->getter(); + if (!getter) + return Value::undefinedValue(); + + return getter->call(ctx, thisObject, 0, 0); +} + +void Object::putValue(ExecutionContext *ctx, Property *pd, PropertyAttributes attrs, const Value &value) +{ + if (attrs.isAccessor()) { + if (pd->set) { + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + goto reject; + } + + if (!attrs.isWritable()) + goto reject; + + pd->value = value; + return; + + reject: + if (ctx->strictMode) + ctx->throwTypeError(); + +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs) +{ + Value v = get(ctx, name); + Value result; + op(ctx, &result, v, rhs); + put(ctx, name, result); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = getIndexed(ctx, idx, &hasProperty); + Value result; + op(ctx, &result, v, rhs); + putIndexed(ctx, idx, result); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(ctx, op, name, rhs); +} + +void Object::defineDefaultProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->newIdentifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->newIdentifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine->newIdentifier(name), value); +} + +void Object::defineReadonlyProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_ReadOnly); + pd->value = value; +} + +void Object::markObjects(Managed *that) +{ + Object *o = static_cast<Object *>(that); + if (o->prototype) + o->prototype->mark(); + + for (int i = 0; i < o->internalClass->size; ++i) { + const Property &pd = o->memberData[i]; + if (o->internalClass->propertyData[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } + o->markArrayObjects(); +} + +Property *Object::insertMember(String *s, PropertyAttributes attributes) +{ + uint idx; + internalClass = internalClass->addMember(s, attributes, &idx); + + if (idx >= memberDataAlloc) { + memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); + Property *newMemberData = new Property[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(Property)*idx); + if (memberData != inlineProperties) + delete [] memberData; + memberData = newMemberData; + } + return memberData + idx; +} + +// Section 8.12.1 +Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getOwnProperty__(idx, attrs); + + uint member = internalClass->find(name); + if (member < UINT_MAX) { + if (attrs) + *attrs = internalClass->propertyData[member]; + return memberData + member; + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = arrayData + pidx; + if (!arrayAttributes || arrayAttributes[pidx].isData()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } else if (arrayAttributes[pidx].isAccessor()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor); + return p; + } + } + if (isStringObject()) { + if (attrs) + *attrs = Attr_NotConfigurable|Attr_NotWritable; + return static_cast<StringObject *>(this)->getIndex(index); + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +// Section 8.12.2 +Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getPropertyDescriptor__(idx); + + + const Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (attrs) + *attrs = o->internalClass->propertyData[idx]; + return o->memberData + idx; + } + + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const +{ + const Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + if (attrs) + *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } + } + if (o->isStringObject()) { + Property *p = static_cast<const StringObject *>(o)->getIndex(index); + if (p) { + if (attrs) + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return p; + } + } + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Value Object::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty) +{ + return static_cast<Object *>(m)->internalGet(ctx, name, hasProperty); +} + +Value Object::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty) +{ + return static_cast<Object *>(m)->internalGetIndexed(ctx, index, hasProperty); +} + +void Object::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value) +{ + static_cast<Object *>(m)->internalPut(ctx, name, value); +} + +void Object::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value) +{ + static_cast<Object *>(m)->internalPutIndexed(ctx, index, value); +} + +PropertyAttributes Object::query(Managed *m, ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, ctx, idx); + + const Object *o = static_cast<Object *>(m); + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) + return o->internalClass->propertyData[idx]; + + o = o->prototype; + } + return Attr_Invalid; +} + +PropertyAttributes Object::queryIndexed(Managed *m, ExecutionContext *ctx, uint index) +{ + const Object *o = static_cast<Object *>(m); + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (o->arrayAttributes) + return o->arrayAttributes[pidx]; + return Attr_Data; + } + if (o->isStringObject()) { + Property *p = static_cast<const StringObject *>(o)->getIndex(index); + if (p) + return Attr_Data; + } + o = o->prototype; + } + return Attr_Invalid; +} + +bool Object::deleteProperty(Managed *m, ExecutionContext *ctx, String *name) +{ + return static_cast<Object *>(m)->internalDeleteProperty(ctx, name); +} + +bool Object::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index) +{ + return static_cast<Object *>(m)->internalDeleteIndexedProperty(ctx, index); +} + + +// Section 8.12.3 +Value Object::internalGet(ExecutionContext *ctx, String *name, bool *hasProperty) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return getIndexed(ctx, idx, hasProperty); + + name->makeIdentifier(ctx); + + if (name->isEqualTo(ctx->engine->id___proto__)) { + if (hasProperty) + *hasProperty = true; + return Value::fromObject(prototype); + } + + Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, o->memberData + idx, o->internalClass->propertyData.at(idx)); + } + + o = o->prototype; + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + +Value Object::internalGetIndexed(ExecutionContext *ctx, uint index, bool *hasProperty) +{ + Property *pd = 0; + PropertyAttributes attrs = Attr_Data; + Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + pd = o->arrayData + pidx; + if (o->arrayAttributes) + attrs = o->arrayAttributes[pidx]; + break; + } + } + if (o->isStringObject()) { + pd = static_cast<StringObject *>(o)->getIndex(index); + if (pd) { + attrs = (Attr_NotWritable|Attr_NotConfigurable); + break; + } + } + o = o->prototype; + } + + if (pd) { + if (hasProperty) + *hasProperty = true; + return getValue(ctx, pd, attrs); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + +// Section 8.12.5 +void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return putIndexed(ctx, idx, value); + + name->makeIdentifier(ctx); + + uint member = internalClass->find(name); + Property *pd = 0; + PropertyAttributes attrs; + if (member < UINT_MAX) { + pd = memberData + member; + attrs = internalClass->propertyData[member]; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + bool ok; + uint l = value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(value); + ok = setArrayLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(name, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(ctx, Value::fromObject(this), args, 1); + return; + } + + { + Property *p = insertMember(name, Attr_Data); + p->value = value; + return; + } + + reject: + if (ctx->strictMode) + ctx->throwTypeError(); +} + +void Object::internalPutIndexed(ExecutionContext *ctx, uint index, const Value &value) +{ + Property *pd = 0; + PropertyAttributes attrs; + + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) { + pidx = UINT_MAX; + } else { + pd = arrayData + pidx; + attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + } + } + + if (!pd && isStringObject()) { + pd = static_cast<StringObject *>(this)->getIndex(index); + if (pd) + // not writable + goto reject; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(index, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(ctx, Value::fromObject(this), args, 1); + return; + } + + arraySet(index, value); + return; + + reject: + if (ctx->strictMode) + ctx->throwTypeError(); +} + +// Section 8.12.7 +bool Object::internalDeleteProperty(ExecutionContext *ctx, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return deleteIndexedProperty(ctx, idx); + + name->makeIdentifier(ctx); + + uint memberIdx = internalClass->find(name); + if (memberIdx != UINT_MAX) { + if (internalClass->propertyData[memberIdx].isConfigurable()) { + internalClass->removeMember(this, name->identifier); + memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); + return true; + } + if (ctx->strictMode) + ctx->throwTypeError(); + return false; + } + + return true; +} + +bool Object::internalDeleteIndexedProperty(ExecutionContext *ctx, uint index) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return true; + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) + return true; + + if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { + arrayData[pidx].value = Value::undefinedValue(); + if (!arrayAttributes) + ensureArrayAttributes(); + arrayAttributes[pidx].clear(); + if (sparseArray) { + arrayData[pidx].value.int_32 = arrayFreeList; + arrayFreeList = pidx; + } + return true; + } + + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __defineOwnProperty__(ctx, idx, p, attrs); + + name->makeIdentifier(ctx); + + Property *current; + PropertyAttributes *cattrs; + + if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); + Property *lp = memberData + ArrayObject::LengthPropertyIndex; + cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex; + if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) + return true; + if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + goto reject; + bool succeeded = true; + if (attrs.type() == PropertyAttributes::Data) { + bool ok; + uint l = p.value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(p.value); + succeeded = setArrayLength(l); + } + if (attrs.hasWritable() && !attrs.isWritable()) + cattrs->setWritable(false); + if (!succeeded) + goto reject; + return true; + } + + // Clause 1 + { + uint member = internalClass->find(name); + current = (member < UINT_MAX) ? memberData + member : 0; + cattrs = internalClass->propertyData.data() + member; + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = insertMember(name, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, name, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) +{ + Property *current = 0; + + // 15.4.5.1, 4b + if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + goto reject; + + if (isNonStrictArgumentsObject) + return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, p, attrs); + + // Clause 1 + { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric())) + current = arrayData + pidx; + if (!current && isStringObject()) + current = static_cast<StringObject *>(this)->getIndex(index); + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = arrayInsert(index, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs) +{ + // clause 5 + if (attrs.isEmpty()) + return true; + + PropertyAttributes cattrs = Attr_Data; + if (member) + cattrs = internalClass->propertyData[current - memberData]; + else if (arrayAttributes) + cattrs = arrayAttributes[current - arrayData]; + + // clause 6 + if (p.isSubset(attrs, *current, cattrs)) + return true; + + // clause 7 + if (!cattrs.isConfigurable()) { + if (attrs.isConfigurable()) + goto reject; + if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) + goto reject; + } + + // clause 8 + if (attrs.isGeneric()) + goto accept; + + // clause 9 + if (cattrs.isData() != attrs.isData()) { + // 9a + if (!cattrs.isConfigurable()) + goto reject; + if (cattrs.isData()) { + // 9b + cattrs.setType(PropertyAttributes::Accessor); + cattrs.clearWritable(); + current->setGetter(0); + current->setSetter(0); + } else { + // 9c + cattrs.setType(PropertyAttributes::Data); + cattrs.setWritable(false); + current->value = Value::undefinedValue(); + } + } else if (cattrs.isData() && attrs.isData()) { // clause 10 + if (!cattrs.isConfigurable() && !cattrs.isWritable()) { + if (attrs.isWritable() || !current->value.sameValue(p.value)) + goto reject; + } + } else { // clause 10 + assert(cattrs.isAccessor() && attrs.isAccessor()); + if (!cattrs.isConfigurable()) { + if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) + goto reject; + if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) + goto reject; + } + } + + accept: + + current->merge(cattrs, p, attrs); + if (member) { + internalClass = internalClass->changeMember(member, cattrs); + } else { + if (cattrs != Attr_Data) + ensureArrayAttributes(); + if (arrayAttributes) + arrayAttributes[current - arrayData] = cattrs; + } + return true; + reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) +{ + return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs); +} + + +void Object::copyArrayData(Object *other) +{ + arrayReserve(other->arrayDataLen); + arrayDataLen = other->arrayDataLen; + memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); + arrayOffset = 0; + if (other->sparseArray) { + sparseArray = new SparseArray(*other->sparseArray); + arrayFreeList = other->arrayFreeList; + } + if (isArrayObject()) + setArrayLengthUnchecked(other->arrayLength()); +} + + +Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +{ + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (protoHasArray || o->arrayAttributes) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->getIndexed(ctx, i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparseArray) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { + Value value = o->getValue(ctx, arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + if ((int) endIndex > arrayDataLen) + endIndex = arrayDataLen; + Property *pd = arrayData; + Property *end = pd + endIndex; + pd += fromIndex; + while (pd < end) { + if (!arrayAttributes || !arrayAttributes[pd - arrayData].isGeneric()) { + Value value = o->getValue(ctx, pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - arrayData); + } + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Object::arrayConcat(const ArrayObject *other) +{ + int newLen = arrayDataLen + other->arrayLength(); + if (other->sparseArray) + initSparse(); + // ### copy attributes as well! + if (sparseArray) { + if (other->sparseArray) { + for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) + arraySet(arrayDataLen + it->key(), other->arrayData + it->value); + } else { + int oldSize = arrayDataLen; + arrayReserve(oldSize + other->arrayLength()); + memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); + for (uint i = 0; i < other->arrayLength(); ++i) { + SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = arrayLength(); + arrayReserve(oldSize + other->arrayDataLen); + if (oldSize > arrayDataLen) { + ensureArrayAttributes(); + std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes()); + } + arrayDataLen = oldSize + other->arrayDataLen; + if (other->arrayAttributes) { + for (int i = 0; i < arrayDataLen; ++i) { + bool exists; + arrayData[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(internalClass->engine->current, i, &exists); + if (arrayAttributes) + arrayAttributes[oldSize + i] = Attr_Data; + if (!exists) { + ensureArrayAttributes(); + arrayAttributes[oldSize + i].clear(); + } + } + } else { + memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); + } + } + setArrayLengthUnchecked(newLen); +} + +void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +{ + if (!arrayDataLen) + return; + + if (sparseArray) { + context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); + return; + } + + if (len > arrayDataLen) + len = arrayDataLen; + + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (arrayAttributes) { + for (uint i = 0; i < len; i++) { + if (arrayAttributes[i].isGeneric()) { + while (--len > i) + if (!arrayAttributes[len].isGeneric()) + break; + arrayData[i].value = getValue(context, arrayData + len, arrayAttributes[len]); + arrayAttributes[i] = Attr_Data; + arrayAttributes[len].clear(); + } else if (arrayAttributes[i].isAccessor()) { + arrayData[i].value = getValue(context, arrayData + i, arrayAttributes[i]); + arrayAttributes[i] = Attr_Data; + } + } + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + Property *begin = arrayData; + std::sort(begin, begin + len, lessThan); +} + + +void Object::initSparse() +{ + if (!sparseArray) { + sparseArray = new SparseArray; + for (int i = 0; i < arrayDataLen; ++i) { + if (!arrayAttributes || !arrayAttributes[i].isGeneric()) { + SparseArrayNode *n = sparseArray->insert(i); + n->value = i + arrayOffset; + } + } + + uint off = arrayOffset; + if (!arrayOffset) { + arrayFreeList = arrayDataLen; + } else { + arrayFreeList = 0; + arrayData -= off; + arrayAlloc += off; + int o = off; + for (int i = 0; i < o - 1; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); + } + for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + } +} + +void Object::arrayReserve(uint n) +{ + if (n < 8) + n = 8; + if (n >= arrayAlloc) { + uint off; + if (sparseArray) { + assert(arrayFreeList == arrayAlloc); + // ### FIXME + arrayDataLen = arrayAlloc; + off = 0; + } else { + off = arrayOffset; + } + arrayAlloc = qMax(n, 2*arrayAlloc); + Property *newArrayData = new Property[arrayAlloc]; + if (arrayData) { + memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen); + delete [] (arrayData - off); + } + arrayData = newArrayData; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) { + arrayData[i].value = Value::deletedValue(); + arrayData[i].value = Value::fromInt32(i + 1); + } + } else { + arrayOffset = 0; + } + + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; + memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); + delete [] (arrayAttributes - off); + + arrayAttributes = newAttrs; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; + } + } + } +} + +void Object::ensureArrayAttributes() +{ + if (arrayAttributes) + return; + + arrayAttributes = new PropertyAttributes[arrayAlloc]; + for (uint i = 0; i < arrayDataLen; ++i) + arrayAttributes[i] = Attr_Data; + for (uint i = arrayDataLen; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; +} + + +bool Object::setArrayLength(uint newLen) { + assert(isArrayObject()); + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + return false; + uint oldLen = arrayLength(); + bool ok = true; + if (newLen < oldLen) { + if (sparseArray) { + SparseArrayNode *begin = sparseArray->lowerBound(newLen); + if (begin != sparseArray->end()) { + SparseArrayNode *it = sparseArray->end()->previousNode(); + while (1) { + Property &pd = arrayData[it->value]; + if (arrayAttributes) { + if (!arrayAttributes[it->value].isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } else { + arrayAttributes[it->value].clear(); + } + } + pd.value.tag = Value::_Deleted_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparseArray->erase(it); + if (brk) + break; + it = prev; + } + } + } else { + Property *it = arrayData + arrayDataLen; + const Property *begin = arrayData + newLen; + while (--it >= begin) { + if (arrayAttributes) { + if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { + ok = false; + newLen = it - arrayData + 1; + break; + } else { + arrayAttributes[it - arrayData].clear(); + } + it->value = Value::deletedValue(); + } + } + arrayDataLen = newLen; + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setArrayLengthUnchecked(newLen); + return ok; +} + +void Object::markArrayObjects() const +{ + for (uint i = 0; i < arrayDataLen; ++i) { + const Property &pd = arrayData[i]; + if (!arrayAttributes || arrayAttributes[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (arrayAttributes[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } +} + +void ArrayObject::init(ExecutionContext *context) +{ + type = Type_ArrayObject; + internalClass = context->engine->arrayClass; + + memberData = new Property[4]; + memberData[LengthPropertyIndex].value = Value::fromInt32(0); +} + + +DEFINE_MANAGED_VTABLE(ForEachIteratorObject); + +void ForEachIteratorObject::markObjects(Managed *that) +{ + ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that); + Object::markObjects(that); + if (o->it.object) + o->it.object->mark(); +} diff --git a/src/qml/qml/v4vm/qv4object.h b/src/qml/qml/v4vm/qv4object.h new file mode 100644 index 0000000000..d1bac5f03f --- /dev/null +++ b/src/qml/qml/v4vm/qv4object.h @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qv4global.h" +#include "qv4runtime.h" +#include "qv4engine.h" +#include "qv4context.h" +#include "qv4sparsearray.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4property.h" +#include "qv4internalclass.h" +#include "qv4objectiterator.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct Value; +struct Function; +struct Lookup; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct CallContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + + +struct Q_V4_EXPORT Object: Managed { + + class ExternalResource { + public: + virtual ~ExternalResource() {} + }; + + Object *prototype; + InternalClass *internalClass; + uint memberDataAlloc; + Property *memberData; + + union { + uint arrayFreeList; + uint arrayOffset; + }; + uint arrayDataLen; + uint arrayAlloc; + PropertyAttributes *arrayAttributes; + Property *arrayData; + SparseArray *sparseArray; + ExternalResource *externalResource; + + enum { + InlinePropertySize = 4 + }; + Property inlineProperties[InlinePropertySize]; + + Object(ExecutionEngine *engine); + Object(ExecutionContext *context); + ~Object(); + + Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); + Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); + + Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; + Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; + + bool __hasProperty__(String *name) const { + return __getPropertyDescriptor__(name); + } + bool __hasProperty__(uint index) const { + return __getPropertyDescriptor__(index); + } + + bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); + + // + // helpers + // + void put(ExecutionContext *ctx, const QString &name, const Value &value); + + static Value getValue(const Value &thisObject, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs); + Value getValue(ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) const { + return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p, attrs); + } + + void putValue(ExecutionContext *ctx, Property *pd, PropertyAttributes attrs, const Value &value); + + void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); + + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(String *name, Value value); + + Property *insertMember(String *s, PropertyAttributes attributes); + + // Array handling + + uint allocArrayValue() { + uint idx = arrayFreeList; + if (arrayAlloc <= arrayFreeList) + arrayReserve(arrayAlloc + 1); + arrayFreeList = arrayData[arrayFreeList].value.uint_32; + if (arrayAttributes) + arrayAttributes[idx].setType(PropertyAttributes::Data); + return idx; + } + + uint allocArrayValue(Value v) { + uint idx = allocArrayValue(); + Property *pd = &arrayData[idx]; + pd->value = v; + return idx; + } + void freeArrayValue(int idx) { + Property &pd = arrayData[idx]; + pd.value.tag = Value::_Deleted_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = idx; + if (arrayAttributes) + arrayAttributes[idx].clear(); + } + + void getArrayHeadRoom() { + assert(!sparseArray && !arrayOffset); + arrayOffset = qMax(arrayDataLen >> 2, (uint)16); + Property *newArray = new Property[arrayOffset + arrayAlloc]; + memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); + delete [] arrayData; + arrayData = newArray + arrayOffset; + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; + memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); + delete [] arrayAttributes; + arrayAttributes = newAttrs + arrayOffset; + } + } + +public: + void copyArrayData(Object *other); + void initSparse(); + + uint arrayLength() const; + bool setArrayLength(uint newLen); + + void setArrayLengthUnchecked(uint l); + + Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) { + + Property *pd; + if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { + if (index >= arrayAlloc) + arrayReserve(index + 1); + if (index >= arrayDataLen) { + ensureArrayAttributes(); + for (uint i = arrayDataLen; i < index; ++i) + arrayAttributes[i].clear(); + arrayDataLen = index + 1; + } + pd = arrayData + index; + } else { + initSparse(); + SparseArrayNode *n = sparseArray->insert(index); + if (n->value == UINT_MAX) + n->value = allocArrayValue(); + pd = arrayData + n->value; + } + if (index >= arrayLength()) + setArrayLengthUnchecked(index + 1); + if (arrayAttributes || attributes != Attr_Data) { + if (!arrayAttributes) + ensureArrayAttributes(); + attributes.resolve(); + arrayAttributes[pd - arrayData] = attributes; + } + return pd; + } + + void arraySet(uint index, const Property *pd) { + *arrayInsert(index) = *pd; + } + + void arraySet(uint index, Value value) { + Property *pd = arrayInsert(index); + pd->value = value; + } + + uint propertyIndexFromArrayIndex(uint index) const + { + if (!sparseArray) { + if (index >= arrayDataLen) + return UINT_MAX; + return index; + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (!n) + return UINT_MAX; + return n->value; + } + } + + Property *arrayAt(uint index) const { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return 0; + return arrayData + pidx; + } + + Property *nonSparseArrayAt(uint index) const { + if (sparseArray) + return 0; + if (index >= arrayDataLen) + return 0; + return arrayData + index; + } + + void markArrayObjects() const; + + void push_back(Value v) { + uint idx = arrayLength(); + if (!sparseArray) { + if (idx >= arrayAlloc) + arrayReserve(idx + 1); + arrayData[idx].value = v; + arrayDataLen = idx + 1; + } else { + uint idx = allocArrayValue(v); + sparseArray->push_back(idx, arrayLength()); + } + setArrayLengthUnchecked(idx + 1); + } + + SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } + SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } + + void arrayConcat(const ArrayObject *other); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); + + void arrayReserve(uint n); + void ensureArrayAttributes(); + + using Managed::get; + using Managed::getIndexed; + using Managed::put; + using Managed::putIndexed; + using Managed::deleteProperty; + using Managed::deleteIndexedProperty; +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty); + static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value); + static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value); + static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name); + static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index); + static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name); + static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index); + +private: + Value internalGet(ExecutionContext *ctx, String *name, bool *hasProperty); + Value internalGetIndexed(ExecutionContext *ctx, uint index, bool *hasProperty); + void internalPut(ExecutionContext *ctx, String *name, const Value &value); + void internalPutIndexed(ExecutionContext *ctx, uint index, const Value &value); + bool internalDeleteProperty(ExecutionContext *ctx, String *name); + bool internalDeleteIndexedProperty(ExecutionContext *ctx, uint index); + + friend struct ObjectIterator; + friend struct ObjectPrototype; +}; + +struct ForEachIteratorObject: Object { + ObjectIterator it; + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { + vtbl = &static_vtbl; + type = Type_ForeachIteratorObject; + } + + Value nextPropertyName() { return it.nextPropertyNameAsString(); } + +protected: + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; } +}; + +struct ArrayObject: Object { + enum { + LengthPropertyIndex = 0 + }; + + ArrayObject(ExecutionContext *ctx) : Object(ctx->engine) { init(ctx); } + void init(ExecutionContext *context); +}; + +inline uint Object::arrayLength() const +{ + if (isArrayObject()) { + // length is always the first property of an array + Value v = memberData[ArrayObject::LengthPropertyIndex].value; + if (v.isInteger()) + return v.integerValue(); + return Value::toUInt32(v.doubleValue()); + } + return 0; +} + +inline void Object::setArrayLengthUnchecked(uint l) +{ + if (isArrayObject()) { + // length is always the first property of an array + Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + lengthProperty.value = Value::fromUInt32(l); + } +} + + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4vm/qv4objectiterator.cpp b/src/qml/qml/v4vm/qv4objectiterator.cpp new file mode 100644 index 0000000000..44ba9efcaa --- /dev/null +++ b/src/qml/qml/v4vm/qv4objectiterator.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4objectiterator.h" +#include "qv4object.h" +#include "qv4stringobject.h" +#include "qv4identifier.h" + +namespace QQmlJS { +namespace VM { + +ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) + : context(context) + , object(o) + , current(o) + , arrayNode(0) + , arrayIndex(0) + , memberIndex(0) + , flags(flags) +{ + if (current) { + if (current->asStringObject()) + this->flags |= CurrentIsString; + } +} + +Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs) +{ + Property *p = 0; + *name = 0; + *index = UINT_MAX; + while (1) { + if (!current) + break; + + if (flags & CurrentIsString) { + StringObject *s = static_cast<StringObject *>(current); + uint slen = s->value.stringValue()->toQString().length(); + while (arrayIndex < slen) { + *index = arrayIndex; + ++arrayIndex; + if (attrs) + *attrs = s->arrayAttributes ? s->arrayAttributes[arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); + return s->__getOwnProperty__(*index); + } + flags &= ~CurrentIsString; + arrayNode = current->sparseArrayBegin(); + // iterate until we're past the end of the string + while (arrayNode && arrayNode->key() < slen) + arrayNode = arrayNode->nextNode(); + } + + if (!arrayIndex) + arrayNode = current->sparseArrayBegin(); + + // sparse arrays + if (arrayNode) { + while (arrayNode != current->sparseArrayEnd()) { + int k = arrayNode->key(); + uint pidx = arrayNode->value; + p = current->arrayData + pidx; + arrayNode = arrayNode->nextNode(); + PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + if (!(flags & EnumberableOnly) || a.isEnumerable()) { + arrayIndex = k + 1; + *index = k; + if (attrs) + *attrs = a; + return p; + } + } + arrayNode = 0; + arrayIndex = UINT_MAX; + } + // dense arrays + while (arrayIndex < current->arrayDataLen) { + uint pidx = current->propertyIndexFromArrayIndex(arrayIndex); + p = current->arrayData + pidx; + PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + ++arrayIndex; + if ((!current->arrayAttributes || !current->arrayAttributes[pidx].isGeneric()) + && (!(flags & EnumberableOnly) || a.isEnumerable())) { + *index = arrayIndex - 1; + if (attrs) + *attrs = a; + return p; + } + } + + if (memberIndex == current->internalClass->size) { + if (flags & WithProtoChain) + current = current->prototype; + else + current = 0; + if (current && current->asStringObject()) + flags |= CurrentIsString; + else + flags &= ~CurrentIsString; + + + arrayIndex = 0; + memberIndex = 0; + continue; + } + String *n = current->internalClass->nameMap.at(memberIndex); + assert(n); + // ### check that it's not a repeated attribute + + p = current->memberData + memberIndex; + PropertyAttributes a = current->internalClass->propertyData[memberIndex]; + ++memberIndex; + if (!(flags & EnumberableOnly) || a.isEnumerable()) { + *name = n; + if (attrs) + *attrs = a; + return p; + } + } + return 0; +} + +Value ObjectIterator::nextPropertyName() +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return Value::fromDouble(index); + return Value::nullValue(); +} + +Value ObjectIterator::nextPropertyNameAsString() +{ + uint index; + String *name; + next(&name, &index); + if (name) + return Value::fromString(name); + if (index < UINT_MAX) + return __qmljs_to_string(Value::fromDouble(index), context); + return Value::nullValue(); +} + +} +} + diff --git a/src/qml/qml/v4vm/qv4objectiterator.h b/src/qml/qml/v4vm/qv4objectiterator.h new file mode 100644 index 0000000000..c740132661 --- /dev/null +++ b/src/qml/qml/v4vm/qv4objectiterator.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4OBJECTITERATOR_H +#define QV4OBJECTITERATOR_H + +#include "qv4value.h" +#include <qv4internalclass.h> +#include <qv4property.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct SparseArrayNode; +struct Object; + +struct ObjectIterator +{ + enum Flags { + NoFlags = 0, + EnumberableOnly = 0x1, + WithProtoChain = 0x2, + CurrentIsString = 0x4 + }; + + ExecutionContext *context; + Object *object; + Object *current; + SparseArrayNode *arrayNode; + uint arrayIndex; + uint memberIndex; + uint flags; + + ObjectIterator(ExecutionContext *context, Object *o, uint flags); + Property *next(String **name, uint *index, PropertyAttributes *attributes = 0); + Value nextPropertyName(); + Value nextPropertyNameAsString(); +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4objectproto.cpp b/src/qml/qml/v4vm/qv4objectproto.cpp new file mode 100644 index 0000000000..df3d4c8e43 --- /dev/null +++ b/src/qml/qml/v4vm/qv4objectproto.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4objectproto.h" +#include "qv4mm.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QQmlJS::VM; + + +DEFINE_MANAGED_VTABLE(ObjectCtor); + +ObjectCtor::ObjectCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value ObjectCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc) +{ + ObjectCtor *ctor = static_cast<ObjectCtor *>(that); + if (!argc || args[0].isUndefined() || args[0].isNull()) { + Object *obj = ctx->engine->newObject(); + Value proto = ctor->get(ctx, ctx->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); + } + return __qmljs_to_object(ctx, args[0]); +} + +Value ObjectCtor::call(Managed *, ExecutionContext *ctx, const Value &/*thisObject*/, Value *args, int argc) +{ + if (!argc || args[0].isUndefined() || args[0].isNull()) + return Value::fromObject(ctx->engine->newObject()); + return __qmljs_to_object(ctx, args[0]); +} + +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); +} + +Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx) +{ + Value o = ctx->argument(0); + if (! o.isObject()) + ctx->throwTypeError(); + + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); +} + +Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyAttributes attrs; + Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs); + return fromPropertyDescriptor(ctx, desc, attrs); +} + +Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) +{ + Object *O = context->argumentCount ? context->arguments[0].asObject() : 0; + if (!O) + context->throwTypeError(); + + ArrayObject *array = context->engine->newArrayObject(context)->asArrayObject(); + ObjectIterator it(context, O, ObjectIterator::NoFlags); + while (1) { + Value v = it.nextPropertyNameAsString(); + if (v.isNull()) + break; + array->push_back(v); + } + return Value::fromObject(array); +} + +Value ObjectPrototype::method_create(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject() && !O.isNull()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.asObject(); + + Value objValue = Value::fromObject(newObject); + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } + + return objValue; +} + +Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + Property pd; + PropertyAttributes attrs; + toPropertyDescriptor(ctx, attributes, &pd, &attrs); + + if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) + ctx->throwTypeError(); + + return O; +} + +Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(1).toObject(ctx); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); + if (!pd) + break; + Property n; + PropertyAttributes nattrs; + toPropertyDescriptor(ctx, o->getValue(ctx, pd, attrs), &n, &nattrs); + bool ok; + if (name) + ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); + else + ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); + if (!ok) + ctx->throwTypeError(); + } + + return O; +} + +Value ObjectPrototype::method_seal(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->sealed(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + } + + return ctx->argument(0); +} + +Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->frozen(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + if (o->arrayAttributes[i].isData()) + o->arrayAttributes[i].setWritable(false); + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + return ctx->argument(0); +} + +Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->sealed()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->frozen()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); +} + +Value ObjectPrototype::method_keys(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); + while (1) { + uint index; + String *name; + Property *pd = it.next(&name, &index); + if (!pd) + break; + Value key; + if (name) { + key = Value::fromString(name); + } else { + key = Value::fromDouble(index); + key = __qmljs_to_string(key, ctx); + } + a->push_back(key); + } + + return Value::fromObject(a); +} + +Value ObjectPrototype::method_toString(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx, ctx->thisObject); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } +} + +Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + Value ts = o->get(ctx, ctx->engine->newString(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + ctx->throwTypeError(); + return f->call(ctx, Value::fromObject(o), 0, 0); +} + +Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx) +{ + return Value::fromObject(ctx->thisObject.toObject(ctx)); +} + +Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx) +{ + String *P = ctx->argument(0).toString(ctx); + Object *O = ctx->thisObject.toObject(ctx); + bool r = O->__getOwnProperty__(P) != 0; + return Value::fromBoolean(r); +} + +Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx) +{ + Value V = ctx->argument(0); + if (! V.isObject()) + return Value::fromBoolean(false); + + Object *O = ctx->thisObject.toObject(ctx); + Object *proto = V.objectValue()->prototype; + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); +} + +Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) +{ + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx); + PropertyAttributes attrs; + o->__getOwnProperty__(p, &attrs); + return Value::fromBoolean(attrs.isEnumerable()); +} + +Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.toObject(ctx); + + Property pd = Property::fromAccessor(f, 0); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.toObject(ctx); + + Property pd = Property::fromAccessor(0, f); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) +{ + if (!v.isObject()) + ctx->throwTypeError(); + + Object *o = v.objectValue(); + + attrs->clear(); + desc->setGetter(0); + desc->setSetter(0); + + if (o->__hasProperty__(ctx->engine->id_enumerable)) + attrs->setEnumerable(o->get(ctx, ctx->engine->id_enumerable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_configurable)) + attrs->setConfigurable(o->get(ctx, ctx->engine->id_configurable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_get)) { + Value get = o->get(ctx, ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + desc->setGetter(f); + } else if (get.isUndefined()) { + desc->setGetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_set)) { + Value set = o->get(ctx, ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); + if (f) { + desc->setSetter(f); + } else if (set.isUndefined()) { + desc->setSetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_writable)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + attrs->setWritable(o->get(ctx, ctx->engine->id_writable).toBoolean()); + // writable forces it to be a data descriptor + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx->engine->id_value)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + desc->value = o->get(ctx, ctx->engine->id_value); + attrs->setType(PropertyAttributes::Data); + } + + if (attrs->isGeneric()) + desc->value = Value::deletedValue(); +} + + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + Property pd; + if (attrs.isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isWritable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); + } else { + pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); + pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); + } + pd.value = Value::fromBoolean(attrs.isEnumerable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isConfigurable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); + + return Value::fromObject(o); +} diff --git a/src/qml/qml/v4vm/qv4objectproto.h b/src/qml/qml/v4vm/qv4objectproto.h new file mode 100644 index 0000000000..0a19f45b06 --- /dev/null +++ b/src/qml/qml/v4vm/qv4objectproto.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ObjectPrototype: Object +{ + ObjectPrototype(ExecutionEngine *engine) : Object(engine) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_getPrototypeOf(SimpleCallContext *ctx); + static Value method_getOwnPropertyDescriptor(SimpleCallContext *ctx); + static Value method_getOwnPropertyNames(SimpleCallContext *context); + static Value method_create(SimpleCallContext *ctx); + static Value method_defineProperty(SimpleCallContext *ctx); + static Value method_defineProperties(SimpleCallContext *ctx); + static Value method_seal(SimpleCallContext *ctx); + static Value method_freeze(SimpleCallContext *ctx); + static Value method_preventExtensions(SimpleCallContext *ctx); + static Value method_isSealed(SimpleCallContext *ctx); + static Value method_isFrozen(SimpleCallContext *ctx); + static Value method_isExtensible(SimpleCallContext *ctx); + static Value method_keys(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_hasOwnProperty(SimpleCallContext *ctx); + static Value method_isPrototypeOf(SimpleCallContext *ctx); + static Value method_propertyIsEnumerable(SimpleCallContext *ctx); + + static Value method_defineGetter(SimpleCallContext *ctx); + static Value method_defineSetter(SimpleCallContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs); +}; + + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4property.h b/src/qml/qml/v4vm/qv4property.h new file mode 100644 index 0000000000..afb5ede277 --- /dev/null +++ b/src/qml/qml/v4vm/qv4property.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYDESCRIPTOR_H +#define QV4PROPERTYDESCRIPTOR_H + +#include "qv4global.h" +#include "qv4value.h" +#include "qv4internalclass.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct FunctionObject; + +struct Property { + union { + Value value; + struct { + FunctionObject *get; + FunctionObject *set; + }; + }; + + // Section 8.10 + inline void fullyPopulated(PropertyAttributes *attrs) { + if (!attrs->hasType()) { + value = Value::undefinedValue(); + } + if (attrs->type() == PropertyAttributes::Accessor) { + attrs->clearWritable(); + if (get == (FunctionObject *)0x1) + get = 0; + if (set == (FunctionObject *)0x1) + set = 0; + } + attrs->resolve(); + } + + static inline Property fromValue(Value v) { + Property pd; + pd.value = v; + return pd; + } + static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) { + Property pd; + pd.get = getter; + pd.set = setter; + return pd; + } + + static Property genericDescriptor() { + Property pd; + pd.value = Value::deletedValue(); + return pd; + } + + inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const; + inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs); + + inline FunctionObject *getter() const { return get; } + inline FunctionObject *setter() const { return set; } + inline void setGetter(FunctionObject *g) { get = g; } + inline void setSetter(FunctionObject *s) { set = s; } +}; + +inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const +{ + if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type()) + return false; + if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable()) + return false; + if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable()) + return false; + if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable()) + return false; + if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value)) + return false; + if (attrs.type() == PropertyAttributes::Accessor) { + if (get != other.get) + return false; + if (set != other.set) + return false; + } + return true; +} + +inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) +{ + if (otherAttrs.hasEnumerable()) + attrs.setEnumerable(otherAttrs.isEnumerable()); + if (otherAttrs.hasConfigurable()) + attrs.setConfigurable(otherAttrs.isConfigurable()); + if (otherAttrs.hasWritable()) + attrs.setWritable(otherAttrs.isWritable()); + if (otherAttrs.type() == PropertyAttributes::Accessor) { + attrs.setType(PropertyAttributes::Accessor); + if (other.get) + get = (other.get == (FunctionObject *)0x1) ? 0 : other.get; + if (other.set) + set = (other.set == (FunctionObject *)0x1) ? 0 : other.set; + } else if (otherAttrs.type() == PropertyAttributes::Data){ + attrs.setType(PropertyAttributes::Data); + value = other.value; + } +} + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4regexp.cpp b/src/qml/qml/v4vm/qv4regexp.cpp new file mode 100644 index 0000000000..c0f7cee51d --- /dev/null +++ b/src/qml/qml/v4vm/qv4regexp.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexp.h" + +#include "qv4engine.h" + +namespace QQmlJS { +namespace VM { + +RegExpCache::~RegExpCache() +{ + for (RegExpCache::Iterator it = begin(), e = end(); + it != e; ++it) + it.value()->m_cache = 0; + clear(); +} + +DEFINE_MANAGED_VTABLE(RegExp); + +uint RegExp::match(const QString &string, int start, uint *matchOffsets) const +{ + if (!isValid()) + return JSC::Yarr::offsetNoMatch; + + return JSC::Yarr::interpret(m_byteCode.get(), WTF::String(string).characters16(), string.length(), start, matchOffsets); +} + +RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline) +{ + RegExpCacheKey key(pattern, ignoreCase, multiline); + + RegExpCache *cache = engine->regExpCache; + if (cache) { + if (RegExp *result = cache->value(key)) + return result; + } + + RegExp *result = new (engine->memoryManager) RegExp(engine, pattern, ignoreCase, multiline); + + if (!cache) + cache = engine->regExpCache = new RegExpCache; + + result->m_cache = cache; + cache->insert(key, result); + + return result; +} + +RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) + : m_pattern(pattern) + , m_cache(0) + , m_subPatternCount(0) + , m_ignoreCase(ignoreCase) + , m_multiLine(multiline) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + if (!engine) + return; + const char* error = 0; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + if (error) + return; + m_subPatternCount = yarrPattern.m_numSubpatterns; + m_byteCode = JSC::Yarr::byteCompile(yarrPattern, &engine->bumperPointerAllocator); +} + +RegExp::~RegExp() +{ + if (m_cache) { + RegExpCacheKey key(this); + m_cache->remove(key); + } + _data = 0; +} + +void RegExp::destroy(Managed *that) +{ + static_cast<RegExp*>(that)->~RegExp(); +} + +void RegExp::markObjects(Managed *that) +{ +} + +Value RegExp::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +Value RegExp::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +void RegExp::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value) +{ +} + +void RegExp::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value) +{ +} + +PropertyAttributes RegExp::query(Managed *m, ExecutionContext *ctx, String *name) +{ + return Attr_Invalid; +} + +PropertyAttributes RegExp::queryIndexed(Managed *m, ExecutionContext *ctx, uint index) +{ + return Attr_Invalid; +} + +bool RegExp::deleteProperty(Managed *m, ExecutionContext *ctx, String *name) +{ + return false; +} + +bool RegExp::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index) +{ + return false; +} + +} // end of namespace VM +} // end of namespace QQmlJS + + diff --git a/src/qml/qml/v4vm/qv4regexp.h b/src/qml/qml/v4vm/qv4regexp.h new file mode 100644 index 0000000000..b0c95843f3 --- /dev/null +++ b/src/qml/qml/v4vm/qv4regexp.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXP_H +#define QV4REGEXP_H + +#include <QString> +#include <QVector> + +#include <wtf/RefPtr.h> +#include <wtf/FastAllocBase.h> +#include <wtf/BumpPointerAllocator.h> + +#include <limits.h> + +#include <yarr/Yarr.h> +#include <yarr/YarrInterpreter.h> + +#include "qv4managed.h" +#include "qv4engine.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct ExecutionEngine; + +struct RegExpCacheKey +{ + RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine) + : pattern(pattern) + , ignoreCase(ignoreCase) + , multiLine(multiLine) + { } + explicit inline RegExpCacheKey(const RegExp *re); + + bool operator==(const RegExpCacheKey &other) const + { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; } + bool operator!=(const RegExpCacheKey &other) const + { return !operator==(other); } + + QString pattern; + uint ignoreCase : 1; + uint multiLine : 1; +}; + +inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW +{ return qHash(key.pattern, seed); } + +class RegExpCache : public QHash<RegExpCacheKey, RegExp*> +{ +public: + ~RegExpCache(); +}; + +class RegExp : public Managed +{ +public: + static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); + ~RegExp(); + + QString pattern() const { return m_pattern; } + + bool isValid() const { return m_byteCode.get(); } + + uint match(const QString& string, int start, uint *matchOffsets) const; + + bool ignoreCase() const { return m_ignoreCase; } + bool multiLine() const { return m_multiLine; } + int captureCount() const { return m_subPatternCount + 1; } + +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty); + static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value); + static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value); + static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name); + static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index); + static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name); + static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index); + + +private: + friend class RegExpCache; + Q_DISABLE_COPY(RegExp); + RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + + const QString m_pattern; + OwnPtr<JSC::Yarr::BytecodePattern> m_byteCode; + RegExpCache *m_cache; + int m_subPatternCount; + const bool m_ignoreCase; + const bool m_multiLine; +}; + +inline RegExpCacheKey::RegExpCacheKey(const RegExp *re) + : pattern(re->pattern()) + , ignoreCase(re->ignoreCase()) + , multiLine(re->multiLine()) +{} + + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4REGEXP_H diff --git a/src/qml/qml/v4vm/qv4regexpobject.cpp b/src/qml/qml/v4vm/qv4regexpobject.cpp new file mode 100644 index 0000000000..3119382e69 --- /dev/null +++ b/src/qml/qml/v4vm/qv4regexpobject.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexpobject.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include "qv4mm.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include "qv4alloca_p.h" + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(RegExpObject); + +RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global) + : Object(engine) + , value(value) + , global(global) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), + Attr_NotEnumerable|Attr_NotConfigurable); + lastIndexProperty->value = Value::fromInt32(0); + if (!this->value) + return; + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(this->value->pattern()))); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); +} + +void RegExpObject::destroy(Managed *that) +{ + static_cast<RegExpObject *>(that)->~RegExpObject(); +} + +void RegExpObject::markObjects(Managed *that) +{ + RegExpObject *re = static_cast<RegExpObject*>(that); + if (re->value) + re->value->mark(); + Object::markObjects(that); +} + +Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx) +{ + assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); + return &memberData[0]; +} + +DEFINE_MANAGED_VTABLE(RegExpCtor); + +RegExpCtor::RegExpCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value RegExpCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) +{ + Value r = argc > 0 ? argv[0] : Value::undefinedValue(); + Value f = argc > 1 ? argv[1] : Value::undefinedValue(); + if (RegExpObject *re = r.asRegExpObject()) { + if (!f.isUndefined()) + ctx->throwTypeError(); + + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + return Value::fromObject(o); + } + + if (r.isUndefined()) + r = Value::fromString(ctx, QString()); + else if (!r.isString()) + r = __qmljs_to_string(r, ctx); + + bool global = false; + bool ignoreCase = false; + bool multiLine = false; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; + } else { + ctx->throwSyntaxError(0); + } + } + } + + RegExp* re = RegExp::create(ctx->engine, r.stringValue()->toQString(), ignoreCase, multiLine); + if (!re->isValid()) + ctx->throwSyntaxError(0); + + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + return Value::fromObject(o); +} + +Value RegExpCtor::call(Managed *that, ExecutionContext *ctx, const Value &thisObject, Value *argv, int argc) +{ + if (argc > 0 && argv[0].asRegExpObject()) { + if (argc == 1 || argv[1].isUndefined()) + return argv[0]; + } + + return construct(that, ctx, argv, argc); +} + +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); +} + +Value RegExpPrototype::method_exec(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject(ctx)->asArrayObject(); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); + if (start != -1 && end != -1) + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->push_back(entry); + } + + array->put(ctx, QLatin1String("index"), Value::fromInt32(result)); + array->put(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]); + + return Value::fromObject(array); +} + +Value RegExpPrototype::method_test(SimpleCallContext *ctx) +{ + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); +} + +Value RegExpPrototype::method_toString(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + QString result = QChar('/') + r->value->pattern(); + result += QChar('/'); + // ### 'g' option missing + if (r->value->ignoreCase()) + result += QChar('i'); + if (r->value->multiLine()) + result += QChar('m'); + return Value::fromString(ctx, result); +} + +Value RegExpPrototype::method_compile(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.asRegExpObject(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ctx->arguments, ctx->argumentCount).asRegExpObject(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + diff --git a/src/qml/qml/v4vm/qv4regexpobject.h b/src/qml/qml/v4vm/qv4regexpobject.h new file mode 100644 index 0000000000..df4190ba77 --- /dev/null +++ b/src/qml/qml/v4vm/qv4regexpobject.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXPOBJECT_H +#define QV4REGEXPOBJECT_H + +#include "qv4runtime.h" +#include "qv4engine.h" +#include "qv4context.h" +#include "qv4functionobject.h" +#include "qv4string.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed.h" +#include "qv4property.h" +#include "qv4objectiterator.h" +#include "qv4regexp.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct RegExpObject: Object { + RegExp* value; + Property *lastIndexProperty(ExecutionContext *ctx); + bool global; + RegExpObject(ExecutionEngine *engine, RegExp* value, bool global); + ~RegExpObject() {} + +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); + static void markObjects(Managed *that); +}; + + +struct RegExpCtor: FunctionObject +{ + RegExpCtor(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(engine, QString()), false) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_exec(SimpleCallContext *ctx); + static Value method_test(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_compile(SimpleCallContext *ctx); +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4vm/qv4runtime.cpp b/src/qml/qml/v4vm/qv4runtime.cpp new file mode 100644 index 0000000000..9cd92677f6 --- /dev/null +++ b/src/qml/qml/v4vm/qv4runtime.cpp @@ -0,0 +1,1319 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4global.h" +#include "debugging.h" +#include "qv4runtime.h" +#include "qv4object.h" +#include "qv4jsir_p.h" +#include "qv4objectproto.h" +#include "qv4globalobject.h" +#include "qv4stringobject.h" +#include "qv4lookup.h" +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> +#include <QtCore/QDebug> +#include <cstdio> +#include <cassert> +#include <typeinfo> +#include <stdlib.h> + +#include "../3rdparty/double-conversion/double-conversion.h" + +#if USE(LIBUNWIND_DEBUG) +#include <libunwind.h> +#include <execinfo.h> +#endif + +namespace QQmlJS { +namespace VM { + +QString numberToString(double num, int radix = 10) +{ + if (isnan(num)) { + return QStringLiteral("NaN"); + } else if (qIsInf(num)) { + return QLatin1String(num < 0 ? "-Infinity" : "Infinity"); + } + + if (radix == 10) { + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); + return QString::fromLatin1(builder.Finalize()); + } + + QString str; + bool negative = false; + + if (num < 0) { + negative = true; + num = -num; + } + + double frac = num - ::floor(num); + num = Value::toInteger(num); + + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + + if (negative) + str.prepend(QLatin1Char('-')); + + return str; +} + +Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue) +{ + this->throwingContext = throwingContext->engine->current; + this->exception = PersistentValue(throwingContext->engine->memoryManager, exceptionValue); + accepted = false; +} + +Exception::~Exception() +{ + assert(accepted); +} + +void Exception::accept(ExecutionContext *catchingContext) +{ + assert(!accepted); + accepted = true; + partiallyUnwindContext(catchingContext); +} + +void Exception::partiallyUnwindContext(ExecutionContext *catchingContext) +{ + if (!throwingContext) + return; + ExecutionContext *context = throwingContext; + while (context != catchingContext) + context = context->engine->popContext(); + throwingContext = context; +} + +extern "C" { + +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos) +{ + assert(clos); + *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); +} + +Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); + + f->hasDirectEval = hasDirectEval; + f->usesArgumentsObject = usesArgumentsObject; + f->isStrict = isStrict; + f->hasNestedFunctions = hasNestedFunctions; + + for (unsigned i = 0; i < formalCount; ++i) + if (formals[i]) + f->formals.append(formals[i]); + for (unsigned i = 0; i < localCount; ++i) + if (locals[i]) + f->locals.append(locals[i]); + + return f; +} + +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) +{ + if (Object *o = base.asObject()) { + uint n = index.asArrayIndex(); + if (n < UINT_MAX) { + Value res = Value::fromBoolean(o->deleteIndexedProperty(ctx, n)); + if (result) + *result = res; + return; + } + } + + String *name = index.toString(ctx); + __qmljs_delete_member(ctx, result, base, name); +} + +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(ctx); + Value res = Value::fromBoolean(obj->deleteProperty(ctx, name)); + if (result) + *result = res; +} + +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + Value res = Value::fromBoolean(ctx->deleteProperty(name)); + if (result) + *result = res; +} + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Value pleft = __qmljs_to_primitive(left, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, PREFERREDTYPE_HINT); + if (pleft.isString() || pright.isString()) { + if (!pleft.isString()) + pleft = __qmljs_to_string(pleft, ctx); + if (!pright.isString()) + pright = __qmljs_to_string(pright, ctx); + String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); + *result = Value::fromString(string); + return; + } + double x = __qmljs_to_number(pleft); + double y = __qmljs_to_number(pright); + *result = Value::fromDouble(x + y); +} + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Object *o = right.asObject(); + if (!o) + ctx->throwTypeError(); + + bool r = o->hasInstance(ctx, left); + *result = Value::fromBoolean(r); +} + +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + if (!right.isObject()) + ctx->throwTypeError(); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(s); + *result = Value::fromBoolean(r); +} + +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_and); +} + +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_or); +} + +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_xor); +} + +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_add); +} + +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_sub); +} + +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mul); +} + +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_div); +} + +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mod); +} + +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shl); +} + +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shr); +} + +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_ushr); +} + +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); +} + +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); +} + +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); +} + +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); +} + +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); +} + +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); +} + +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); +} + +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); +} + +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); +} + +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); +} + +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); +} + +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); +} + +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); +} + +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); +} + +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_add, name, rhs); +} + +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); +} + +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); +} + +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_div, name, rhs); +} + +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); +} + +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); +} + +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); +} + +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); +} + +double __qmljs_string_to_number(const String *string) +{ + QString s = string->toQString(); + s = s.trimmed(); + if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) + return s.toLong(0, 16); + bool ok; + QByteArray ba = s.toLatin1(); + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin != ba.size()) { + if (ba == "Infinity" || ba == "+Infinity") + d = Q_INFINITY; + else if (ba == "-Infinity") + d = -Q_INFINITY; + else + d = std::numeric_limits<double>::quiet_NaN(); + } + return d; +} + +Value __qmljs_string_from_number(ExecutionContext *ctx, double number) +{ + String *string = ctx->engine->newString(numberToString(number, 10)); + return Value::fromString(string); +} + +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) +{ + const QString &a = first->toQString(); + const QString &b = second->toQString(); + QString newStr(a.length() + b.length(), Qt::Uninitialized); + QChar *data = newStr.data(); + memcpy(data, a.constData(), a.length()*sizeof(QChar)); + data += a.length(); + memcpy(data, b.constData(), b.length()*sizeof(QChar)); + + return ctx->engine->newString(newStr); +} + +Value __qmljs_object_default_value(Object *object, int typeHint) +{ + if (typeHint == PREFERREDTYPE_HINT) { + if (object->asDateObject()) + typeHint = STRING_HINT; + else + typeHint = NUMBER_HINT; + } + + ExecutionEngine *engine = object->internalClass->engine; + String *meth1 = engine->newString("toString"); + String *meth2 = engine->newString("valueOf"); + + if (typeHint == NUMBER_HINT) + qSwap(meth1, meth2); + + ExecutionContext *ctx = engine->current; + + Value conv = object->get(ctx, meth1); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(ctx, Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + conv = object->get(ctx, meth2); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(ctx, Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Bool __qmljs_to_boolean(const Value &value) +{ + return value.toBoolean(); +} + + +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) +{ + assert(!value.isObject()); + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + ctx->throwTypeError(); + case Value::Boolean_Type: + return ctx->engine->newBooleanObject(value); + case Value::String_Type: + return ctx->engine->newStringObject(ctx, value); + break; + case Value::Object_Type: + Q_UNREACHABLE(); + case Value::Integer_Type: + default: // double + return ctx->engine->newNumberObject(value); + } +} + +String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) +{ + switch (value.type()) { + case Value::Undefined_Type: + return ctx->engine->id_undefined; + case Value::Null_Type: + return ctx->engine->id_null; + case Value::Boolean_Type: + if (value.booleanValue()) + return ctx->engine->id_true; + else + return ctx->engine->id_false; + case Value::String_Type: + return value.stringValue(); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, STRING_HINT); + if (prim.isPrimitive()) + return __qmljs_convert_to_string(ctx, prim); + else + ctx->throwTypeError(); + } + case Value::Integer_Type: + return __qmljs_string_from_number(ctx, value.int_32).stringValue(); + default: // double + return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue(); + } // switch +} + +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) +{ + Object *o = object.toObject(ctx); + o->put(ctx, name, value); +} + +void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) +{ + uint idx = index.asArrayIndex(); + + Object *o = object.asObject(); + if (!o) { + if (idx < UINT_MAX) { + if (String *str = object.asString()) { + if (idx >= (uint)str->toQString().length()) { + if (result) + *result = Value::undefinedValue(); + return; + } + const QString s = str->toQString().mid(idx, 1); + if (result) + *result = Value::fromString(ctx, s); + return; + } + } + + o = __qmljs_convert_to_object(ctx, object); + } + + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + if (result) + *result = o->arrayData[pidx].value; + return; + } + } + + Value res = o->getIndexed(ctx, idx); + if (result) + *result = res; + return; + } + + String *name = index.toString(ctx); + Value res = o->get(ctx, name); + if (result) + *result = res; +} + +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) +{ + Object *o = object.toObject(ctx); + + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + p->value = value; + return; + } + + if (o->arrayAttributes[pidx].isAccessor()) { + FunctionObject *setter = p->setter(); + if (!setter) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Value args[1]; + args[0] = value; + setter->call(ctx, Value::fromObject(o), args, 1); + return; + } + } + o->putIndexed(ctx, idx, value); + return; + } + + String *name = index.toString(ctx); + o->put(ctx, name, value); +} + +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in) +{ + Object *o = 0; + if (!in.isNull() && !in.isUndefined()) + o = in.toObject(ctx); + Object *it = ctx->engine->newForEachIteratorObject(ctx, o); + *result = Value::fromObject(it); +} + +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue()); + assert(it->asForeachIteratorObject()); + + *result = it->nextPropertyName(); +} + + +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->setProperty(name, value); +} + +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name) +{ + Value res; + Managed *m = object.asManaged(); + if (m) { + res = m->get(ctx, name); + } else { + m = __qmljs_convert_to_object(ctx, object); + res = m->get(ctx, name); + } + if (result) + *result = res; +} + +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + *result = ctx->getProperty(name); +} + +void __qmljs_get_global_lookup(ExecutionContext *ctx, Value *result, int lookupIndex) +{ + Lookup *l = ctx->lookups + lookupIndex; + l->lookupGlobal(l, ctx, result); +} + +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex) +{ + Lookup *l = ctx->lookups + lookupIndex; + l->lookupProperty(l, ctx, result, object); +} + +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value) +{ + Object *o = object.toObject(ctx); + Lookup *l = ctx->lookups + lookupIndex; + + PropertyAttributes attrs; + Property *p = l->setterLookup(o, &attrs); + if (p && (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject())) { + o->putValue(ctx, p, attrs, value); + return; + } + + o->put(ctx, l->name, value); +} + + +uint __qmljs_equal(const Value &x, const Value &y) +{ + if (x.type() == y.type()) { + switch (x.type()) { + case Value::Undefined_Type: + return true; + case Value::Null_Type: + return true; + case Value::Boolean_Type: + return x.booleanValue() == y.booleanValue(); + break; + case Value::Integer_Type: + return x.integerValue() == y.integerValue(); + case Value::String_Type: + return x.stringValue()->isEqualTo(y.stringValue()); + case Value::Object_Type: + if (x.objectValue()->externalComparison || y.objectValue()->externalComparison) + return x.objectValue()->externalComparison && y.objectValue()->externalComparison + && x.objectValue()->internalClass->engine->externalResourceComparison(x, y); + return x.objectValue() == y.objectValue(); + default: // double + return x.doubleValue() == y.doubleValue(); + } + // unreachable + } else { + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isNull() && y.isUndefined()) { + return true; + } else if (x.isUndefined() && y.isNull()) { + return true; + } else if (x.isNumber() && y.isString()) { + Value ny = Value::fromDouble(__qmljs_to_number(y)); + return __qmljs_equal(x, ny); + } else if (x.isString() && y.isNumber()) { + Value nx = Value::fromDouble(__qmljs_to_number(x)); + return __qmljs_equal(nx, y); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_equal(nx, y); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_equal(x, ny); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT); + return __qmljs_equal(x, py); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT); + return __qmljs_equal(px, y); + } + } + + return false; +} + +Bool __qmljs_strict_equal(const Value &x, const Value &y) +{ + TRACE2(x, y); + + if (x.isDouble() || y.isDouble()) + return x.asDouble() == y.asDouble(); + if (x.rawValue() == y.rawValue()) + return true; + if (x.type() != y.type()) + return false; + if (x.isString()) + return x.stringValue()->isEqualTo(y.stringValue()); + if (x.isObject() && x.objectValue()->externalComparison && y.objectValue()->externalComparison) + return x.objectValue()->internalClass->engine->externalResourceComparison(x, y); + return false; +} + + +void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value v; + l->lookupGlobal(l, context, &v); + FunctionObject *o = v.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value thisObject = Value::undefinedValue(); + + if (o == context->engine->evalFunction && l->name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast<EvalFunction *>(o)->evalCall(context, thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; +} + + +void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Object *base; + Value func = context->getPropertyAndBase(name, &base); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast<EvalFunction *>(o)->evalCall(context, thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc) +{ + Value thisObject = thatObject; + Managed *baseObject = thisObject.asManaged(); + if (!baseObject) { + baseObject = __qmljs_convert_to_object(context, thisObject); + thisObject = Value::fromObject(static_cast<Object *>(baseObject)); + } + + Value func = baseObject->get(context, name); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + + Object *baseObject; + if (thisObject.isObject()) + baseObject = thisObject.objectValue(); + else if (thisObject.isString()) + baseObject = context->engine->stringPrototype; + else + baseObject = __qmljs_convert_to_object(context, thisObject); + + PropertyAttributes attrs; + Property *p = l->lookup(baseObject, &attrs); + if (!p) + context->throwTypeError(); + Value func = attrs.isData() ? p->value : baseObject->getValue(context, p, attrs); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) +{ + Object *baseObject = that.toObject(context); + Value thisObject = Value::fromObject(baseObject); + + Value func = baseObject->get(context, index.toString(context)); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(context, thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) +{ + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + Value res = o->call(context, thisObject ? *thisObject : Value::undefinedValue(), args, argc); + if (result) + *result = res; +} + + +void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value func; + l->lookupGlobal(l, context, &func); + + if (Object *f = func.asObject()) { + Value res = f->construct(context, args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + + +void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Value func = context->getProperty(name); + __qmljs_construct_value(context, result, func, args, argc); +} + +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) +{ + if (Object *f = func.asObject()) { + Value res = f->construct(context, args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) +{ + Object *thisObject = base.toObject(context); + + Value func = thisObject->get(context, name); + if (Object *f = func.asObject()) { + Value res = f->construct(context, args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_throw(ExecutionContext *context, const Value &value) +{ + if (context->engine->debugger) + context->engine->debugger->aboutToThrow(value); + +#if USE(LIBUNWIND_DEBUG) + printf("about to throw exception. walking stack first with libunwind:\n"); + unw_cursor_t cursor; unw_context_t uc; + unw_word_t ip, sp; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0) { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + printf("ip = %lx, sp = %lx ", (long) ip, (long) sp); + void * const addr = (void*)ip; + char **symbol = backtrace_symbols(&addr, 1); + printf("%s", symbol[0]); + free(symbol); + printf("\n"); + } + printf("stack walked. throwing exception now...\n"); +#endif + + throw Exception(context, value); +} + +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) +{ + if (!result) + return; + String *res = 0; + switch (value.type()) { + case Value::Undefined_Type: + res = ctx->engine->id_undefined; + break; + case Value::Null_Type: + res = ctx->engine->id_object; + break; + case Value::Boolean_Type: + res = ctx->engine->id_boolean; + break; + case Value::String_Type: + res = ctx->engine->id_string; + break; + case Value::Object_Type: + if (value.objectValue()->asFunctionObject()) + res = ctx->engine->id_function; + else + res = ctx->engine->id_object; // ### implementation-defined + break; + default: + res = ctx->engine->id_number; + break; + } + *result = Value::fromString(res); +} + +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) +{ + Value res; + __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(context, name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + String *name = index.toString(context); + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(context, name)); + if (result) + *result = res; +} + +void __qmljs_builtin_post_increment(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() < INT_MAX) { + if (result) + *result = *val; + val->int_32 += 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d + 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(context, name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->put(context, name, v); +} + +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) +{ + Object *o = base.toObject(context); + + uint idx = index->asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index->toString(context); + return __qmljs_builtin_post_increment_member(context, result, base, s); + } + + Value v = o->getIndexed(context, idx); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->putIndexed(context, idx, v); +} + +void __qmljs_builtin_post_decrement(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() > INT_MIN) { + if (result) + *result = *val; + val->int_32 -= 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d - 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(context, name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->put(context, name, v); +} + +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + Object *o = base.toObject(context); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_decrement_member(context, result, base, s); + } + + Value v = o->getIndexed(context, idx); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->putIndexed(context, idx, v); +} + +void __qmljs_builtin_throw(ExecutionContext *context, const Value &val) +{ + __qmljs_throw(context, val); +} + +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx) +{ + Object *obj = o.toObject(ctx); + return ctx->engine->newWithContext(obj); +} + +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const Value &exceptionValue, ExecutionContext *ctx) +{ + return ctx->engine->newCatchContext(exceptionVarName, exceptionValue); +} + +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) +{ + return ctx->engine->popContext(); +} + +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + ctx->createMutableBinding(name, deletable); +} + +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); + pd->value = val ? *val : Value::undefinedValue(); +} + +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) +{ + ArrayObject *a = ctx->engine->newArrayObject(ctx); + + // ### FIXME: We need to allocate the array data to avoid crashes other places + // This should rather be done when required + a->arrayReserve(length); + if (length) { + a->arrayDataLen = length; + Property *pd = a->arrayData; + for (uint i = 0; i < length; ++i) { + if (values[i].isDeleted()) { + a->ensureArrayAttributes(); + pd->value = Value::undefinedValue(); + a->arrayAttributes[i].clear(); + } else { + pd->value = values[i]; + } + ++pd; + } + a->setArrayLengthUnchecked(length); + } + *array = Value::fromObject(a); +} + +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); + pd->setGetter(getter ? getter->asFunctionObject() : 0); + pd->setSetter(setter ? setter->asFunctionObject() : 0); +} + +void __qmljs_increment(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() + 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d + 1); + } +} + +void __qmljs_decrement(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() - 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d - 1); + } +} + +} // extern "C" + + +} // namespace VM +} // namespace QQmlJS diff --git a/src/qml/qml/v4vm/qv4runtime.h b/src/qml/qml/v4vm/qv4runtime.h new file mode 100644 index 0000000000..6e64c44e69 --- /dev/null +++ b/src/qml/qml/v4vm/qv4runtime.h @@ -0,0 +1,745 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_RUNTIME_H +#define QMLJS_RUNTIME_H + +#include "qv4global.h" +#include "qv4value.h" +#include "qv4math.h" + + +#include <QtCore/QString> +#include <QtCore/qnumeric.h> +#include <QtCore/QDebug> + +#include <cmath> +#include <cassert> + +#include <wtf/MathExtras.h> + +#ifdef DO_TRACE_INSTR +# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); +# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); +#else +# define TRACE1(x) +# define TRACE2(x, y) +#endif // TRACE1 + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +enum TypeHint { + PREFERREDTYPE_HINT, + NUMBER_HINT, + STRING_HINT +}; + +struct Function; +struct Object; +struct String; +struct ExecutionContext; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct RegExpObject; +struct ArrayObject; +struct ErrorObject; +struct ExecutionEngine; + +struct Q_V4_EXPORT Exception { + explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue); + ~Exception(); + + void accept(ExecutionContext *catchingContext); + + void partiallyUnwindContext(ExecutionContext *catchingContext); + + Value value() const { return exception; } + +private: + ExecutionContext *throwingContext; + bool accepted; + PersistentValue exception; +}; + +extern "C" { + +// context +void __qmljs_call_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &that, String *name, Value *args, int argc); +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc); +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc); +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc); + +void __qmljs_construct_activation_property(ExecutionContext *, Value *result, String *name, Value *args, int argc); +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc); +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc); + +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &val); +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value* result, String *name); +void __qmljs_builtin_typeof_member(ExecutionContext* context, Value* result, const Value &base, String *name); +void __qmljs_builtin_typeof_element(ExecutionContext* context, Value *result, const Value &base, const Value &index); + +void __qmljs_builtin_post_increment(Value *result, Value *val); +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name); +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name); +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index); + +void __qmljs_builtin_post_decrement(Value *result, Value *val); +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name); +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name); +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index); + +void Q_NORETURN __qmljs_builtin_throw(ExecutionContext *context, const Value &val); +void Q_NORETURN __qmljs_builtin_rethrow(ExecutionContext *context); +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const QQmlJS::VM::Value &exceptionValue, ExecutionContext *ctx); +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx); +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name); +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val); +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, QQmlJS::VM::Value *values, uint length); +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter); + +// constructors +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, VM::Function *clos); +VM::Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount); + + +// strings +double __qmljs_string_to_number(const String *string); +Value __qmljs_string_from_number(ExecutionContext *ctx, double number); +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second); + +// objects +Value __qmljs_object_default_value(Object *object, int typeHint); +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value& value); +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value); +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name); +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name); + +void __qmljs_get_global_lookup(ExecutionContext *ctx, Value *result, int lookupIndex); +void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc); +void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc); +void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Value &object, int lookupIndex); +void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int lookupIndex, const Value &value); + + +void __qmljs_get_element(ExecutionContext *ctx, Value *retval, const Value &object, const Value &index); +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value); + +// For each +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in); +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator); + +// type conversion and testing +Value __qmljs_to_primitive(const Value &value, int typeHint); +Bool __qmljs_to_boolean(const Value &value); +double __qmljs_to_number(const Value &value); +Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); +Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); +Value __qmljs_to_object(ExecutionContext *ctx, const Value &value); +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); + +Bool __qmljs_equal(const Value &x, const Value &y); +Bool __qmljs_strict_equal(const Value &x, const Value &y); + +// unary operators +typedef void (*UnaryOpName)(Value *, const Value &); +void __qmljs_uplus(Value *result, const Value &value); +void __qmljs_uminus(Value *result, const Value &value); +void __qmljs_compl(Value *result, const Value &value); +void __qmljs_not(Value *result, const Value &value); +void __qmljs_increment(Value *result, const Value &value); +void __qmljs_decrement(Value *result, const Value &value); + +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index); +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name); +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name); + +void Q_NORETURN __qmljs_throw(ExecutionContext*, const Value &value); + +// binary operators +typedef void (*BinOp)(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_bit_or(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_bit_xor(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_bit_and(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_sub(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_mul(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_div(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_mod(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_shl(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_shr(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_ushr(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); +void __qmljs_se(ExecutionContext *, Value *result, const Value &left, const Value &right); +void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, const Value &right); + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right); + + +typedef void (*InplaceBinOpName)(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value); +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value); + +typedef void (*InplaceBinOpElement)(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs); + +typedef void (*InplaceBinOpMember)(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs); + +typedef Bool (*CmpOp)(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_gt(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_lt(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_ge(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_le(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_eq(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_ne(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_se(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_sne(ExecutionContext *, const Value &left, const Value &right); +Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right); +Bool __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right); + +// type conversion and testing +inline Value __qmljs_to_primitive(const Value &value, int typeHint) +{ + Object *o = value.asObject(); + if (!o) + return value; + return __qmljs_object_default_value(o, typeHint); +} + +inline double __qmljs_to_number(const Value &value) +{ + switch (value.type()) { + case Value::Undefined_Type: + return std::numeric_limits<double>::quiet_NaN(); + case Value::Null_Type: + return 0; + case Value::Boolean_Type: + return (value.booleanValue() ? 1. : 0.); + case Value::Integer_Type: + return value.int_32; + case Value::String_Type: + return __qmljs_string_to_number(value.stringValue()); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, NUMBER_HINT); + return __qmljs_to_number(prim); + } + default: // double + return value.doubleValue(); + } +} + +inline Value __qmljs_to_string(const Value &value, ExecutionContext *ctx) +{ + if (value.isString()) + return value; + return Value::fromString(__qmljs_convert_to_string(ctx, value)); +} + +inline Value __qmljs_to_object(ExecutionContext *ctx, const Value &value) +{ + if (value.isObject()) + return value; + return Value::fromObject(__qmljs_convert_to_object(ctx, value)); +} + + +inline void __qmljs_uplus(Value *result, const Value &value) +{ + TRACE1(value); + + *result = value; + if (result->tryIntegerConversion()) + return; + + double n = __qmljs_to_number(value); + *result = Value::fromDouble(n); +} + +inline void __qmljs_uminus(Value *result, const Value &value) +{ + TRACE1(value); + + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) + *result = Value::fromInt32(-value.integerValue()); + else { + double n = __qmljs_to_number(value); + *result = Value::fromDouble(-n); + } +} + +inline void __qmljs_compl(Value *result, const Value &value) +{ + TRACE1(value); + + int n; + if (value.isConvertibleToInt()) + n = value.int_32; + else + n = Value::toInt32(__qmljs_to_number(value)); + + *result = Value::fromInt32(~n); +} + +inline void __qmljs_not(Value *result, const Value &value) +{ + TRACE1(value); + + bool b = value.toBoolean(); + *result = Value::fromBoolean(!b); +} + +// binary operators +inline void __qmljs_bit_or(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() | right.integerValue()); + return; + } + + int lval = Value::toInt32(__qmljs_to_number(left)); + int rval = Value::toInt32(__qmljs_to_number(right)); + *result = Value::fromInt32(lval | rval); +} + +inline void __qmljs_bit_xor(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() ^ right.integerValue()); + return; + } + + int lval = Value::toInt32(__qmljs_to_number(left)); + int rval = Value::toInt32(__qmljs_to_number(right)); + *result = Value::fromInt32(lval ^ rval); +} + +inline void __qmljs_bit_and(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() & right.integerValue()); + return; + } + + int lval = Value::toInt32(__qmljs_to_number(left)); + int rval = Value::toInt32(__qmljs_to_number(right)); + *result = Value::fromInt32(lval & rval); +} + +inline void __qmljs_add(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + +#ifdef QMLJS_INLINE_MATH + if (Value::integerCompatible(left, right)) { + *result = add_int32(left.integerValue(), right.integerValue()); + return; + } +#endif + + if (Value::bothDouble(left, right)) { + *result = Value::fromDouble(left.doubleValue() + right.doubleValue()); + return; + } + + __qmljs_add_helper(ctx, result, left, right); +} + +inline void __qmljs_sub(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + +#ifdef QMLJS_INLINE_MATH + if (Value::integerCompatible(left, right)) { + *result = sub_int32(left.integerValue(), right.integerValue()); + return; + } +#endif + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = Value::fromDouble(lval - rval); +} + +inline void __qmljs_mul(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + +#ifdef QMLJS_INLINE_MATH + if (Value::integerCompatible(left, right)) { + *result = mul_int32(left.integerValue(), right.integerValue()); + return; + } +#endif + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = Value::fromDouble(lval * rval); +} + +inline void __qmljs_div(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = Value::fromDouble(lval / rval); +} + +inline void __qmljs_mod(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right) && right.integerValue() != 0) { + int intRes = left.integerValue() % right.integerValue(); + if (intRes != 0 || left.integerValue() >= 0) { + *result = Value::fromInt32(intRes); + return; + } + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = Value::fromDouble(fmod(lval, rval)); +} + +inline void __qmljs_shl(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = Value::toInt32(__qmljs_to_number(left)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = Value::fromInt32(lval << rval); +} + +inline void __qmljs_shr(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + if (Value::integerCompatible(left, right)) { + *result = Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = Value::toInt32(__qmljs_to_number(left)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = Value::fromInt32(lval >> rval); +} + +inline void __qmljs_ushr(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + uint res; + if (Value::integerCompatible(left, right)) { + res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + } else { + unsigned lval = Value::toUInt32(__qmljs_to_number(left)); + unsigned rval = Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + res = lval >> rval; + } + + if (res > INT_MAX) + *result = Value::fromDouble(res); + else + *result = Value::fromInt32(res); +} + +inline void __qmljs_gt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right)); +} + +inline void __qmljs_lt(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right)); +} + +inline void __qmljs_ge(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right)); +} + +inline void __qmljs_le(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(__qmljs_cmp_le(ctx, left, right)); +} + +inline void __qmljs_eq(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right)); +} + +inline void __qmljs_ne(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + *result = Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right)); +} + +inline void __qmljs_se(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = __qmljs_strict_equal(left, right); + *result = Value::fromBoolean(r); +} + +inline void __qmljs_sne(ExecutionContext *, Value *result, const Value &left, const Value &right) +{ + TRACE2(left, right); + + bool r = ! __qmljs_strict_equal(left, right); + *result = Value::fromBoolean(r); +} + +inline Bool __qmljs_cmp_gt(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() > right.integerValue(); + + Value l = __qmljs_to_primitive(left, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, NUMBER_HINT); + + if (Value::bothDouble(l, r)) { + return l.doubleValue() > r.doubleValue(); + } else if (l.isString() && r.isString()) { + return r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl > dr; + } +} + +inline Bool __qmljs_cmp_lt(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() < right.integerValue(); + + Value l = __qmljs_to_primitive(left, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, NUMBER_HINT); + + if (Value::bothDouble(l, r)) { + return l.doubleValue() < r.doubleValue(); + } else if (l.isString() && r.isString()) { + return l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl < dr; + } +} + +inline Bool __qmljs_cmp_ge(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() >= right.integerValue(); + + Value l = __qmljs_to_primitive(left, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, NUMBER_HINT); + + if (Value::bothDouble(l, r)) { + return l.doubleValue() >= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl >= dr; + } +} + +inline Bool __qmljs_cmp_le(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + if (Value::integerCompatible(left, right)) + return left.integerValue() <= right.integerValue(); + + Value l = __qmljs_to_primitive(left, NUMBER_HINT); + Value r = __qmljs_to_primitive(right, NUMBER_HINT); + + if (Value::bothDouble(l, r)) { + return l.doubleValue() <= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl <= dr; + } +} + +inline Bool __qmljs_cmp_eq(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + + // need to test for doubles first as NaN != NaN + if (Value::bothDouble(left, right)) + return left.doubleValue() == right.doubleValue(); + if (left.val == right.val) + return true; + if (left.isString() && right.isString()) + return left.stringValue()->isEqualTo(right.stringValue()); + + return __qmljs_equal(left, right); +} + +inline Bool __qmljs_cmp_ne(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + + return !__qmljs_cmp_eq(0, left, right); +} + +inline Bool __qmljs_cmp_se(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + + return __qmljs_strict_equal(left, right); +} + +inline Bool __qmljs_cmp_sne(ExecutionContext *, const Value &left, const Value &right) +{ + TRACE2(left, right); + + return ! __qmljs_strict_equal(left, right); +} + +inline Bool __qmljs_cmp_instanceof(ExecutionContext *ctx, const Value &left, const Value &right) +{ + TRACE2(left, right); + + Value v; + __qmljs_instanceof(ctx, &v, left, right); + return v.booleanValue(); +} + +inline uint __qmljs_cmp_in(ExecutionContext *ctx, const Value &left, const Value &right) +{ + TRACE2(left, right); + + Value v; + __qmljs_in(ctx, &v, left, right); + return v.booleanValue(); +} + +} // extern "C" + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QMLJS_RUNTIME_H diff --git a/src/qml/qml/v4vm/qv4sparsearray.cpp b/src/qml/qml/v4vm/qv4sparsearray.cpp new file mode 100644 index 0000000000..2c9075ba5b --- /dev/null +++ b/src/qml/qml/v4vm/qv4sparsearray.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4sparsearray.h" +#include "qv4runtime.h" +#include "qv4object.h" +#include "qv4functionobject.h" +#include <stdlib.h> + +#ifdef QT_QMAP_DEBUG +# include <qstring.h> +# include <qvector.h> +#endif + +namespace QQmlJS { +namespace VM { + +bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const +{ + Value v1 = p1.value; + Value v2 = p2.value; + + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result = Value::undefinedValue(); + __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2); + return result.toNumber() <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const +{ + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArray::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArray::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArray::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArray::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArray::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast<SparseArrayNode *>(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = UINT_MAX; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArray::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArray::SparseArray() + : numEntries(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +SparseArray::SparseArray(const SparseArray &other) +{ + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} + +SparseArrayNode *SparseArray::insert(uint akey) +{ + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + return n; + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + return createNode(s, y, left); +} + + +} +} diff --git a/src/qml/qml/v4vm/qv4sparsearray.h b/src/qml/qml/v4vm/qv4sparsearray.h new file mode 100644 index 0000000000..a6f7d40c38 --- /dev/null +++ b/src/qml/qml/v4vm/qv4sparsearray.h @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include "qv4global.h" +#include <QtCore/qmap.h> +#include <qv4value.h> +#include <qv4property.h> +#include <assert.h> + +#ifdef Q_MAP_DEBUG +#include <QtCore/qdebug.h> +#endif + +#include <new> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct SparseArray; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const Property &v1, const Property &v2) const; + +private: + ExecutionContext *m_context; + Object *thisObject; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + uint value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast<SparseArrayNode *>(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArray *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_V4_EXPORT SparseArray +{ + SparseArray(); + ~SparseArray() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + SparseArray(const SparseArray &other); +private: + SparseArray &operator=(const SparseArray &other); + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *root() const { return header.left; } + + void deleteNode(SparseArrayNode *z); + + +public: + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *findNode(uint akey) const; + + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); + + QList<int> keys() const; + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + SparseArrayNode *erase(SparseArrayNode *n); + + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif +}; + +inline SparseArrayNode *SparseArray::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + +inline uint SparseArray::pop_front() +{ + uint idx = UINT_MAX ; + + SparseArrayNode *n = findNode(0); + if (n) { + idx = n->value; + deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + return idx; +} + +inline void SparseArray::push_front(uint value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left += 1; + n = n->left; + } + n = insert(0); + n->value = value; +} + +inline uint SparseArray::pop_back(uint len) +{ + uint idx = UINT_MAX; + if (!len) + return idx; + + SparseArrayNode *n = findNode(len - 1); + if (n) { + idx = n->value; + deleteNode(n); + } + return idx; +} + +inline void SparseArray::push_back(uint index, uint len) +{ + SparseArrayNode *n = insert(len); + n->value = index; +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) +{ + if (n == end()) + return n; + + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; +} + +inline QList<int> SparseArray::keys() const +{ + QList<int> res; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); + } + return res; +} + +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const +{ + const SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline SparseArrayNode *SparseArray::lowerBound(uint akey) +{ + SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const +{ + const SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +inline SparseArrayNode *SparseArray::upperBound(uint akey) +{ + SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + +} +} + +QT_END_NAMESPACE + +#endif // QMAP_H diff --git a/src/qml/qml/v4vm/qv4string.cpp b/src/qml/qml/v4vm/qv4string.cpp new file mode 100644 index 0000000000..cb17547f07 --- /dev/null +++ b/src/qml/qml/v4vm/qv4string.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4string.h" +#include "qv4identifier.h" +#include "qv4runtime.h" +#include "qv4objectproto.h" +#include "qv4stringobject.h" +#include <QtCore/QHash> + +namespace QQmlJS { +namespace VM { + +static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) +{ + *ok = false; + uint i = ch->unicode() - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = ch->unicode() - '0'; + if (x > 9) + return UINT_MAX; + uint n = i*10 + x; + if (n < i) + // overflow + return UINT_MAX; + i = n; + ++ch; + } + *ok = true; + return i; +} + +const ManagedVTable String::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + hasInstance, + get, + getIndexed, + put, + putIndexed, + query, + queryIndexed, + deleteProperty, + deleteIndexedProperty, + "String", +}; + +void String::destroy(Managed *that) +{ + static_cast<String*>(that)->~String(); +} + +Value String::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty) +{ + String *that = static_cast<String *>(m); + if (name == ctx->engine->id_length) { + if (hasProperty) + *hasProperty = true; + return Value::fromInt32(that->_text.length()); + } + PropertyAttributes attrs; + Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(name, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs); +} + +Value String::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty) +{ + String *that = static_cast<String *>(m); + if (index < that->_text.length()) { + if (hasProperty) + *hasProperty = true; + return Value::fromString(ctx, that->toQString().mid(index, 1)); + } + PropertyAttributes attrs; + Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(index, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs); +} + +void String::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value) +{ + String *that = static_cast<String *>(m); + Object *o = ctx->engine->newStringObject(ctx, Value::fromString(that)); + o->put(ctx, name, value); +} + +void String::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value) +{ + String *that = static_cast<String *>(m); + Object *o = ctx->engine->newStringObject(ctx, Value::fromString(that)); + o->putIndexed(ctx, index, value); +} + +PropertyAttributes String::query(Managed *m, ExecutionContext *ctx, String *name) +{ + return Attr_Invalid; +} + +PropertyAttributes String::queryIndexed(Managed *m, ExecutionContext *ctx, uint index) +{ + String *that = static_cast<String *>(m); + return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid; +} + +bool String::deleteProperty(Managed *m, ExecutionContext *ctx, String *name) +{ + return false; +} + +bool String::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index) +{ + return false; +} + +uint String::toUInt(bool *ok) const +{ + *ok = true; + + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype >= StringType_UInt) + return stringHash; + + // ### this conversion shouldn't be required + double d = __qmljs_string_to_number(this); + uint l = (uint)d; + if (d == l) + return l; + *ok = false; + return UINT_MAX; +} + +void String::makeIdentifierImpl(const ExecutionContext *ctx) +{ + ctx->engine->identifierCache->toIdentifier(this); +} + +void String::createHashValue() const +{ + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + + // array indices get their number as hash value + bool ok; + stringHash = toArrayIndex(ch, end, &ok); + if (ok) { + subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; + return; + } + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + stringHash = h; + subtype = StringType_Regular; +} + +uint String::createHashValue(const QChar *ch, int length) +{ + const QChar *end = ch + length; + + // array indices get their number as hash value + bool ok; + uint stringHash = toArrayIndex(ch, end, &ok); + if (ok) + return stringHash; + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + return h; +} + +} +} diff --git a/src/qml/qml/v4vm/qv4string.h b/src/qml/qml/v4vm/qv4string.h new file mode 100644 index 0000000000..f4eeb7d0a8 --- /dev/null +++ b/src/qml/qml/v4vm/qv4string.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRING_H +#define QV4STRING_H + +#include <QtCore/qstring.h> +#include "qv4managed.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct ExecutionEngine; + +struct String : public Managed { + enum StringType { + StringType_Unknown, + StringType_Regular, + StringType_UInt, + StringType_ArrayIndex + }; + + String(const QString &text) + : _text(text), stringHash(UINT_MAX), identifier(UINT_MAX) + { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } + ~String() { _data = 0; } + + inline bool isEqualTo(const String *other) const { + if (this == other) + return true; + if (hashValue() != other->hashValue()) + return false; + if (identifier != UINT_MAX && identifier == other->identifier) + return true; + if (subtype >= StringType_UInt && subtype == other->subtype) + return true; + + return toQString() == other->toQString(); + } + inline bool compare(const String *other) { + return toQString() < other->toQString(); + } + + inline const QString &toQString() const { + return _text; + } + + inline unsigned hashValue() const { + if (subtype == StringType_Unknown) + createHashValue(); + + return stringHash; + } + uint asArrayIndex() const { + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype == StringType_ArrayIndex) + return stringHash; + return UINT_MAX; + } + uint toUInt(bool *ok) const; + + void makeIdentifier(const ExecutionContext *ctx) { + if (identifier != UINT_MAX) + return; + makeIdentifierImpl(ctx); + } + + void makeIdentifierImpl(const ExecutionContext *ctx); + + void createHashValue() const; + static uint createHashValue(const QChar *ch, int length); + + QString _text; + mutable uint stringHash; + mutable uint identifier; + +protected: + static void destroy(Managed *); + static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty); + static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value); + static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value); + static PropertyAttributes query(Managed *m, ExecutionContext *ctx, String *name); + static PropertyAttributes queryIndexed(Managed *m, ExecutionContext *ctx, uint index); + static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name); + static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index); + + static const ManagedVTable static_vtbl; +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/qv4stringobject.cpp b/src/qml/qml/v4vm/qv4stringobject.cpp new file mode 100644 index 0000000000..42b9b422ec --- /dev/null +++ b/src/qml/qml/v4vm/qv4stringobject.cpp @@ -0,0 +1,726 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4stringobject.h" +#include "qv4regexpobject.h" +#include "qv4objectproto.h" +#include "qv4mm.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QQmlJS::VM; + +DEFINE_MANAGED_VTABLE(StringObject); + +StringObject::StringObject(ExecutionContext *ctx, const Value &value) + : Object(ctx->engine), value(value) +{ + vtbl = &static_vtbl; + type = Type_StringObject; + + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +Property *StringObject::getIndex(uint index) const +{ + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = internalClass->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} + +void StringObject::markObjects(Managed *that) +{ + StringObject *o = static_cast<StringObject *>(that); + o->value.stringValue()->mark(); + Object::markObjects(that); +} + +DEFINE_MANAGED_VTABLE(StringCtor); + +StringCtor::StringCtor(ExecutionContext *scope) + : FunctionObject(scope) +{ + vtbl = &static_vtbl; +} + +Value StringCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(ctx)); + else + value = Value::fromString(ctx, QString()); + return Value::fromObject(ctx->engine->newStringObject(ctx, value)); +} + +Value StringCtor::call(Managed *, ExecutionContext *parentCtx, const Value &thisObject, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(parentCtx)); + else + value = Value::fromString(parentCtx, QString()); + return value; +} + +void StringPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("fromCharCode"), method_fromCharCode, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical + defineDefaultProperty(ctx, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(ctx, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(ctx, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(ctx, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(ctx, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(ctx, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(ctx, QStringLiteral("substring"), method_substring, 2); + defineDefaultProperty(ctx, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(ctx, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(ctx, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(ctx, QStringLiteral("trim"), method_trim); +} + +static QString getThisString(ExecutionContext *ctx) +{ + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + ctx->throwTypeError(); + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); +} + +static QString getThisString(ExecutionContext *context, Value thisObject) +{ + if (thisObject.isString()) + return thisObject.stringValue()->toQString(); + + String* str = 0; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + context->throwTypeError(); + else + str = thisObject.toString(context); + return str->toQString(); +} + +Value StringPrototype::method_toString(SimpleCallContext *context) +{ + if (context->thisObject.isString()) + return context->thisObject; + + StringObject *o = context->thisObject.asStringObject(); + if (!o) + context->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_charAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + return Value::fromString(context, result); +} + +Value StringPrototype::method_charCodeAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + + if (pos >= 0 && pos < str.length()) + return Value::fromInt32(str.at(pos).unicode()); + + return Value::fromDouble(qSNaN()); +} + +Value StringPrototype::method_concat(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + for (int i = 0; i < context->argumentCount; ++i) { + Value v = __qmljs_to_string(context->arguments[i], context); + assert(v.isString()); + value += v.stringValue()->toQString(); + } + + return Value::fromString(context, value); +} + +Value StringPrototype::method_indexOf(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) + searchString = context->arguments[0].toString(context)->toQString(); + + int pos = 0; + if (context->argumentCount > 1) + pos = (int) context->arguments[1].toInteger(); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + return Value::fromDouble(index); +} + +Value StringPrototype::method_lastIndexOf(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) { + Value v = __qmljs_to_string(context->arguments[0], context); + searchString = v.stringValue()->toQString(); + } + + Value posArg = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + double position = __qmljs_to_number(posArg); + if (isnan(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + if (searchString.isNull() && pos == 0) + return Value::fromDouble(-1); + int index = value.lastIndexOf(searchString, pos); + return Value::fromDouble(index); +} + +Value StringPrototype::method_localeCompare(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + const QString that = (context->argumentCount ? context->arguments[0] : Value::undefinedValue()).toString(context)->toQString(); + return Value::fromDouble(QString::localeAwareCompare(value, that)); +} + +Value StringPrototype::method_match(SimpleCallContext *context) +{ + if (context->thisObject.isUndefined() || context->thisObject.isNull()) + context->throwTypeError(); + + String *s = context->thisObject.toString(context); + + Value regexp = context->argumentCount ? context->arguments[0] : Value::undefinedValue(); + RegExpObject *rx = regexp.asRegExpObject(); + if (!rx) + rx = context->engine->regExpCtor.asFunctionObject()->construct(context, ®exp, 1).asRegExpObject(); + + if (!rx) + // ### CHECK + context->throwTypeError(); + + bool global = rx->global; + + // ### use the standard builtin function, not the one that might be redefined in the proto + FunctionObject *exec = context->engine->regExpPrototype->get(context, context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(context, Value::fromObject(rx), &arg, 1); + + String *lastIndex = context->engine->newString(QStringLiteral("lastIndex")); + rx->put(context, lastIndex, Value::fromInt32(0)); + ArrayObject *a = context->engine->newArrayObject(context); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(context, Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->get(context, lastIndex, 0).toInteger(); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->put(context, lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->getIndexed(context, 0, (bool *)0); + a->arraySet(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + +} + +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + +Value StringPrototype::method_replace(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray<uint, 16> matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.asRegExpObject(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(ctx, Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_search(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.asRegExpObject(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx, ®ExpValue, 1); + regExp = regExpValue.asRegExpObject(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); +} + +Value StringPrototype::method_slice(SimpleCallContext *ctx) +{ + const QString text = getThisString(ctx); + const double length = text.length(); + + double start = ctx->argument(0).toInteger(); + double end = ctx->argument(1).isUndefined() + ? length : ctx->argument(1).toInteger(); + + if (start < 0) + start = qMax(length + start, 0.); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0.); + else + end = qMin(end, length); + + const int intStart = int(start); + const int intEnd = int(end); + + int count = qMax(0, intEnd - intStart); + return Value::fromString(ctx, text.mid(intStart, count)); +} + +Value StringPrototype::method_split(SimpleCallContext *ctx) +{ + QString text; + if (StringObject *thisObject = ctx->thisObject.asStringObject()) + text = thisObject->value.stringValue()->toQString(); + else + text = ctx->thisObject.toString(ctx)->toQString(); + + Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + + ArrayObject* array = ctx->engine->newArrayObject(ctx); + Value result = Value::fromObject(array); + + if (separatorValue.isUndefined()) { + if (limitValue.isUndefined()) { + array->push_back(Value::fromString(ctx, text)); + return result; + } + return Value::fromString(ctx, text.left(limitValue.toInteger())); + } + + uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(); + + if (limit == 0) + return result; + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + if (re->value->pattern().isEmpty()) { + re = 0; + separatorValue = Value::fromString(ctx, QString()); + } + } + + if (RegExpObject* re = separatorValue.asRegExpObject()) { + uint offset = 0; + uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); + while (true) { + uint result = re->value->match(text, offset, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + break; + + array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + offset = qMax(offset + 1, matchOffsets[1]); + + if (array->arrayLength() >= limit) + break; + + for (int i = 1; i < re->value->captureCount(); ++i) { + uint start = matchOffsets[i * 2]; + uint end = matchOffsets[i * 2 + 1]; + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->arrayLength() >= limit) + break; + } + } + if (array->arrayLength() < limit) + array->push_back(Value::fromString(ctx, text.mid(offset))); + } else { + QString separator = separatorValue.toString(ctx)->toQString(); + if (separator.isEmpty()) { + for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + array->push_back(Value::fromString(ctx, text.mid(i, 1))); + return result; + } + + int start = 0; + int end; + while ((end = text.indexOf(separator, start)) != -1) { + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + start = end + separator.size(); + if (array->arrayLength() >= limit) + break; + } + if (array->arrayLength() < limit && start != -1) + array->push_back(Value::fromString(ctx, text.mid(start))); + } + return result; +} + +Value StringPrototype::method_substr(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + double start = 0; + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + double length = +qInf(); + if (context->argumentCount > 1) + length = context->arguments[1].toInteger(); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_substring(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + int length = value.length(); + + double start = 0; + double end = length; + + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + Value endValue = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + if (!endValue.isUndefined()) + end = endValue.toInteger(); + + if (isnan(start) || start < 0) + start = 0; + + if (isnan(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = (int)start; + qint32 y = (int)(end - start); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_toLowerCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toLower()); +} + +Value StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx) +{ + return method_toLowerCase(ctx); +} + +Value StringPrototype::method_toUpperCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toUpper()); +} + +Value StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx) +{ + return method_toUpperCase(ctx); +} + +Value StringPrototype::method_fromCharCode(SimpleCallContext *context) +{ + QString str(context->argumentCount, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < context->argumentCount; ++i) { + *ch = QChar(context->arguments[i].toUInt16()); + ++ch; + } + return Value::fromString(context, str); +} + +Value StringPrototype::method_trim(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + ctx->throwTypeError(); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} diff --git a/src/qml/qml/v4vm/qv4stringobject.h b/src/qml/qml/v4vm/qv4stringobject.h new file mode 100644 index 0000000000..ded26c501b --- /dev/null +++ b/src/qml/qml/v4vm/qv4stringobject.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRINGOBJECT_P_H +#define QV4STRINGOBJECT_P_H + +#include "qv4object.h" +#include "qv4functionobject.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct StringObject: Object { + Value value; + mutable Property tmpProperty; + StringObject(ExecutionContext *ctx, const Value &value); + + Property *getIndex(uint index) const; + +protected: + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(ExecutionContext *scope); + + static Value construct(Managed *that, ExecutionContext *context, Value *args, int argc); + static Value call(Managed *that, ExecutionContext *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct StringPrototype: StringObject +{ + StringPrototype(ExecutionContext *ctx): StringObject(ctx, Value::fromString(ctx, QString())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *context); + static Value method_charAt(SimpleCallContext *context); + static Value method_charCodeAt(SimpleCallContext *context); + static Value method_concat(SimpleCallContext *context); + static Value method_indexOf(SimpleCallContext *context); + static Value method_lastIndexOf(SimpleCallContext *context); + static Value method_localeCompare(SimpleCallContext *context); + static Value method_match(SimpleCallContext *context); + static Value method_replace(SimpleCallContext *ctx); + static Value method_search(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_split(SimpleCallContext *ctx); + static Value method_substr(SimpleCallContext *context); + static Value method_substring(SimpleCallContext *context); + static Value method_toLowerCase(SimpleCallContext *ctx); + static Value method_toLocaleLowerCase(SimpleCallContext *ctx); + static Value method_toUpperCase(SimpleCallContext *ctx); + static Value method_toLocaleUpperCase(SimpleCallContext *ctx); + static Value method_fromCharCode(SimpleCallContext *context); + static Value method_trim(SimpleCallContext *ctx); +}; + +} // end of namespace VM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4vm/qv4syntaxchecker.cpp b/src/qml/qml/v4vm/qv4syntaxchecker.cpp new file mode 100644 index 0000000000..fcda486af2 --- /dev/null +++ b/src/qml/qml/v4vm/qv4syntaxchecker.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4syntaxchecker_p.h" + +using namespace QQmlJS; + +SyntaxChecker::SyntaxChecker() + : Lexer(&m_engine) + , m_stateStack(128) +{ +} + +void QQmlJS::SyntaxChecker::clearText() +{ + m_code.clear(); + m_tokens.clear(); +} + +void SyntaxChecker::appendText(const QString &text) +{ + m_code += text; +} + +QString SyntaxChecker::text() const +{ + return m_code; +} + +bool SyntaxChecker::canEvaluate() +{ + int yyaction = 0; + int yytoken = -1; + int yytos = -1; + + setCode(m_code, 1); + + m_tokens.clear(); + m_tokens.append(T_FEED_JS_PROGRAM); + + do { + if (++yytos == m_stateStack.size()) + m_stateStack.resize(m_stateStack.size() * 2); + + m_stateStack[yytos] = yyaction; + +again: + if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { + if (m_tokens.isEmpty()) + yytoken = lex(); + else + yytoken = m_tokens.takeFirst(); + } + + yyaction = t_action(yyaction, yytoken); + if (yyaction > 0) { + if (yyaction == ACCEPT_STATE) { + --yytos; + return true; + } + yytoken = -1; + } else if (yyaction < 0) { + const int ruleno = -yyaction - 1; + yytos -= rhs[ruleno]; + yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); + } + } while (yyaction); + + const int errorState = m_stateStack[yytos]; + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { + yyaction = errorState; + m_tokens.prepend(yytoken); + yytoken = T_SEMICOLON; + goto again; + } + + if (yytoken != EOF_SYMBOL) + return true; + + return false; +} diff --git a/src/qml/qml/v4vm/qv4syntaxchecker_p.h b/src/qml/qml/v4vm/qv4syntaxchecker_p.h new file mode 100644 index 0000000000..38e123762e --- /dev/null +++ b/src/qml/qml/v4vm/qv4syntaxchecker_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4SYNTAXCHECKER_P_H +#define QV4SYNTAXCHECKER_P_H + +#include <private/qqmljslexer_p.h> +#include <private/qqmljsengine_p.h> + +#include <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QList> + +namespace QQmlJS { + +class SyntaxChecker: Lexer +{ +public: + SyntaxChecker(); + + QString text() const; + void clearText(); + void appendText(const QString &text); + + bool canEvaluate(); + +private: + Engine m_engine; + QVector<int> m_stateStack; + QList<int> m_tokens; + QString m_code; +}; + +} // end of QQmlJS namespace + +#endif // QV4SYNTAXCHECKER_P_H diff --git a/src/qml/qml/v4vm/qv4unwindhelper.cpp b/src/qml/qml/v4vm/qv4unwindhelper.cpp new file mode 100644 index 0000000000..8a50b0bb53 --- /dev/null +++ b/src/qml/qml/v4vm/qv4unwindhelper.cpp @@ -0,0 +1,37 @@ +#include <qv4unwindhelper.h> + +#include <wtf/Platform.h> + +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X)) +# define USE_DW2_HELPER +#elif CPU(X86) && OS(LINUX) +# define USE_DW2_HELPER +#elif CPU(ARM) && OS(LINUX) +# define USE_ARM_HELPER +#elif OS(WINDOWS) + // SJLJ will unwind on Windows +# define USE_NULL_HELPER +#elif OS(IOS) + // SJLJ will unwind on iOS +# define USE_NULL_HELPER +#else +# warning "Unsupported/untested platform!" +# define USE_NULL_HELPER +#endif + +#ifdef USE_DW2_HELPER +# include <qv4unwindhelper_p-dw2.h> +#endif // USE_DW2_HELPER + +#ifdef USE_ARM_HELPER +# include <qv4unwindhelper_p-arm.h> +#endif // USE_ARM_HELPER + +#ifdef USE_NULL_HELPER +using namespace QQmlJS::VM; +void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::registerFunctions(QVector<Function *> functions) {Q_UNUSED(functions);} +void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::deregisterFunctions(QVector<Function *> functions) {Q_UNUSED(functions);} +#endif // USE_NULL_HELPER + diff --git a/src/qml/qml/v4vm/qv4unwindhelper.h b/src/qml/qml/v4vm/qv4unwindhelper.h new file mode 100644 index 0000000000..9f6462d644 --- /dev/null +++ b/src/qml/qml/v4vm/qv4unwindhelper.h @@ -0,0 +1,27 @@ +#ifndef QV4UNWINDHELPER_H +#define QV4UNWINDHELPER_H + +#include <QtCore/QVector> + +namespace QQmlJS { +namespace VM { + +struct Function; + +class UnwindHelper +{ +public: + static void registerFunction(Function *function); + static void registerFunctions(QVector<Function *> functions); + static void deregisterFunction(Function *function); + static void deregisterFunctions(QVector<Function *> functions); +#ifdef Q_PROCESSOR_ARM + static int unwindInfoSize(); + static void writeARMUnwindInfo(void *codeAddr, int codeSize); +#endif +}; + +} // VM namespace +} // QQmlJS namespace + +#endif // QV4UNWINDHELPER_H diff --git a/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h b/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h new file mode 100644 index 0000000000..6938fa5189 --- /dev/null +++ b/src/qml/qml/v4vm/qv4unwindhelper_p-arm.h @@ -0,0 +1,176 @@ +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include <qv4unwindhelper.h> +#include <qv4functionobject.h> +#include <wtf/Platform.h> + +#include <QMap> +#include <QMutex> + +#define __USE_GNU +#include <dlfcn.h> + +#if USE(LIBUNWIND_DEBUG) +#include <libunwind.h> +#endif +#include <execinfo.h> + +namespace QQmlJS { +namespace VM { + +static void *removeThumbBit(void *addr) +{ + return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(addr) & ~1u); +} + +static QMutex functionProtector; +static QMap<quintptr, Function*> allFunctions; + +static Function *lookupFunction(void *pc) +{ + quintptr key = reinterpret_cast<quintptr>(pc); + QMap<quintptr, Function*>::ConstIterator it = allFunctions.lowerBound(key); + if (it != allFunctions.begin() && allFunctions.count() > 0) + --it; + if (it == allFunctions.end()) + return 0; + + quintptr codeStart = reinterpret_cast<quintptr>(removeThumbBit((*it)->codeRef.code().executableAddress())); + if (key < codeStart || key >= codeStart + (*it)->codeSize) + return 0; + return *it; +} + + +/* Program: +vsp = r4 (REG_TO_SP r4) +vsp -= 8 * 4 -- > vsp = vsp - (7 << 2) - 4 +pop r12, r10, r9, r8, r7, r6, r5, r4 +pop r4 +pop lr +pop r0, r1, r2, r3 +*/ + +#define REG_TO_SP 0b10010000 +#define VSP_MINUS 0b01000000 +#define POP_REG_MULTI 0b10000000 +#define POP_R4_MULTI 0b10100000 +#define POP_R4_R14_MULTI 0b10101000 +#define POP_R0_TO_R3 0b10110001 +#define FINISH 0b10110000 + +#define MK_UW_WORD(first, second, third, fourth) \ + (((first) << 24) | \ + ((second) << 16) | \ + ((third) << 8) | \ + (fourth)) + +static unsigned int extbl[] = { + MK_UW_WORD(0x80 | // High bit set to indicate that this isn't a PREL31 + 2, // Choose personality routine #2 + 2, // Number of 4 byte words used to encode remaining unwind instructions + REG_TO_SP | 4, // Encoded program from above. + VSP_MINUS | 7), + MK_UW_WORD(POP_REG_MULTI | 1, 0b01111111, + POP_R4_R14_MULTI, + POP_R0_TO_R3), + MK_UW_WORD(0b00001111, + FINISH, + FINISH, + FINISH) +}; + +static unsigned write_prel31(unsigned *addr, void *ptr) +{ + int delta = (char *)ptr - (char*)addr; + if (delta < 0) + delta |= (1 << 30); + else + delta &= ~(1 << 30); + *addr = ((unsigned)delta) & 0x7fffffffU; +} + +void UnwindHelper::deregisterFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.remove(reinterpret_cast<quintptr>(function->code)); +} + +void UnwindHelper::deregisterFunctions(QVector<Function *> functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.remove(reinterpret_cast<quintptr>(f->code)); +} + +void UnwindHelper::registerFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.insert(reinterpret_cast<quintptr>(function->code), function); +} + +void UnwindHelper::registerFunctions(QVector<Function *> functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.insert(reinterpret_cast<quintptr>(f->code), f); +} + +int UnwindHelper::unwindInfoSize() +{ + return 2 * sizeof(unsigned int) // 2 extbl entries + + sizeof(extbl); +} + +void UnwindHelper::writeARMUnwindInfo(void *codeAddr, int codeSize) +{ + unsigned int *exidx = (unsigned int *)((char *)codeAddr + codeSize); + + unsigned char *exprog = (unsigned char *)((unsigned char *)codeAddr + codeSize + 8); + + write_prel31(exidx, codeAddr); + exidx[1] = 4; // PREL31 offset to extbl, which follows right afterwards + + memcpy(exprog, extbl, sizeof(extbl)); + +#if USE(LIBUNWIND_DEBUG) + unw_dyn_info_t *info = (unw_dyn_info_t*)malloc(sizeof(unw_dyn_info_t)); + info->start_ip = (unw_word_t)codeAddr; + info->end_ip = info->start_ip + codeSize; + info->gp = 0; + info->format = UNW_INFO_FORMAT_ARM_EXIDX; + info->u.rti.name_ptr = 0; + info->u.rti.segbase = 0; + info->u.rti.table_len = 8; + info->u.rti.table_data = (unw_word_t)exidx; + _U_dyn_register(info); +#endif +} + +} +} + +extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount) +{ + typedef void *(*Old_Unwind_Find_exidx)(void*, int*); + static Old_Unwind_Find_exidx oldFunction = 0; + static ptrdiff_t *exidx = (ptrdiff_t*)malloc(2 * sizeof(uintptr_t)); + if (!oldFunction) + oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx"); + + { + QMutexLocker locker(&QQmlJS::VM::functionProtector); + QQmlJS::VM::Function *function = QQmlJS::VM::lookupFunction(pc); + if (function) { + *entryCount = 1; + void * codeStart = QQmlJS::VM::removeThumbBit(function->codeRef.code().executableAddress()); + // At the end of the function we store our synthetic exception table entry. + return (char *)codeStart + function->codeSize; + } + } + + return oldFunction(pc, entryCount); +} + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h new file mode 100644 index 0000000000..10c99f539c --- /dev/null +++ b/src/qml/qml/v4vm/qv4unwindhelper_p-dw2.h @@ -0,0 +1,189 @@ +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include <qv4unwindhelper.h> +#include <qv4functionobject.h> +#include <wtf/Platform.h> + +#include <QMap> +#include <QMutex> + +#define __USE_GNU +#include <dlfcn.h> + +namespace QQmlJS { +namespace VM { + +namespace { +#if CPU(X86_64) +// Generated by fdegen +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8, + 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86, + 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4, + 0x0, 0x0, 0x0, 0x0 +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 36; +#elif CPU(X86) && OS(LINUX) +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4, + 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85, + 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4, + 0x0, 0x0, 0x0, 0x0, +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 32; +#endif +} // anonymous namespace + +static QMutex functionProtector; +static QMap<quintptr, Function*> allFunctions; + +static Function *lookupFunction(void *pc) +{ + quintptr key = reinterpret_cast<quintptr>(pc); + QMap<quintptr, Function*>::ConstIterator it = allFunctions.lowerBound(key); + if (it != allFunctions.begin() && allFunctions.count() > 0) + --it; + if (it == allFunctions.end()) + return 0; + + quintptr codeStart = reinterpret_cast<quintptr>((*it)->code); + if (key < codeStart || key >= codeStart + (*it)->codeSize) + return 0; + return *it; +} + +namespace { +void writeIntPtrValue(unsigned char *addr, intptr_t val) +{ + addr[0] = (val >> 0) & 0xff; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +#if QT_POINTER_SIZE == 8 + addr[4] = (val >> 32) & 0xff; + addr[5] = (val >> 40) & 0xff; + addr[6] = (val >> 48) & 0xff; + addr[7] = (val >> 56) & 0xff; +#endif +} +} // anonymous namespace + +static void ensureUnwindInfo(Function *f) +{ + if (!f->unwindInfo.isEmpty()) + return; + QByteArray info; + info.resize(sizeof(cie_fde_data)); + + unsigned char *cie_and_fde = reinterpret_cast<unsigned char *>(info.data()); + memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); + + intptr_t ptr = static_cast<char *>(f->codeRef.code().executableAddress()) - static_cast<char *>(0); + writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); + + writeIntPtrValue(cie_and_fde + address_range_offset, f->codeSize); + + f->unwindInfo = info; +} + +#if defined(Q_OS_DARWIN) +extern "C" void __register_frame(void *fde); +extern "C" void __deregister_frame(void *fde); +#endif + +static void registerFunctionUnlocked(Function *f) +{ + allFunctions.insert(reinterpret_cast<quintptr>(f->code), f); +#if defined(Q_OS_DARWIN) + ensureUnwindInfo(f); + __register_frame(f->unwindInfo.data() + fde_offset); +#endif +} + +static void deregisterFunctionUnlocked(Function *f) +{ + allFunctions.remove(reinterpret_cast<quintptr>(f->code)); +#if defined(Q_OS_DARWIN) + if (!f->unwindInfo.isEmpty()) + __deregister_frame(f->unwindInfo.data() + fde_offset); +#endif +} + +void UnwindHelper::registerFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + registerFunctionUnlocked(function); +} + +void UnwindHelper::registerFunctions(QVector<Function *> functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + registerFunctionUnlocked(f); +} + +void UnwindHelper::deregisterFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + deregisterFunctionUnlocked(function); +} + +void UnwindHelper::deregisterFunctions(QVector<Function *> functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + deregisterFunctionUnlocked(f); +} + +} // VM namespace +} // QQmlJS namespace + +#if defined(Q_OS_LINUX) +extern "C" { + +struct bases +{ + void *tbase; + void *dbase; + void *func; +}; + +Q_V4_EXPORT void *_Unwind_Find_FDE(void *pc, struct bases *bases) +{ + typedef void *(*Old_Unwind_Find_FDE)(void *pc, struct bases *bases); + static Old_Unwind_Find_FDE oldFunction = 0; + if (!oldFunction) + oldFunction = (Old_Unwind_Find_FDE)dlsym(RTLD_NEXT, "_Unwind_Find_FDE"); + + { + QMutexLocker locker(&QQmlJS::VM::functionProtector); + QQmlJS::VM::Function *function = QQmlJS::VM::lookupFunction(pc); + if (function) { + bases->tbase = 0; + bases->dbase = 0; + bases->func = reinterpret_cast<void*>(function->code); + QQmlJS::VM::ensureUnwindInfo(function); + return function->unwindInfo.data() + QQmlJS::VM::fde_offset; + } + } + + return oldFunction(pc, bases); +} + +} +#endif + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4vm/qv4util.h b/src/qml/qml/v4vm/qv4util.h new file mode 100644 index 0000000000..c43d85eca7 --- /dev/null +++ b/src/qml/qml/v4vm/qv4util.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4UTIL_H +#define QV4UTIL_H + +#include "qv4global.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +template <typename T> +struct TemporaryAssignment +{ + TemporaryAssignment(T &var, const T& temporaryValue) + : variable(var) + , savedValue(var) + { + variable = temporaryValue; + } + ~TemporaryAssignment() + { + variable = savedValue; + } + T &variable; + T savedValue; +private: + TemporaryAssignment(const TemporaryAssignment<T>&); + TemporaryAssignment operator=(const TemporaryAssignment<T>&); +}; + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4UTIL_H diff --git a/src/qml/qml/v4vm/qv4v8.cpp b/src/qml/qml/v4vm/qv4v8.cpp new file mode 100644 index 0000000000..d81f23f30f --- /dev/null +++ b/src/qml/qml/v4vm/qv4v8.cpp @@ -0,0 +1,2141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4v8.h" + +#include "qv4engine.h" +#include "qv4runtime.h" +#include "qv4mm.h" +#include "qv4managed.h" +#include "qv4functionobject.h" +#include "qv4value.h" +#include "qv4isel_masm_p.h" +#include "qv4globalobject.h" +#include "qv4regexpobject.h" +#include "qv4dateobject.h" +#include "qv4numberobject.h" +#include "qv4booleanobject.h" +#include "qv4stringobject.h" +#include "qv4objectproto.h" +#include <QThreadStorage> + +using namespace QQmlJS; +using namespace QQmlJS::VM; + +namespace v8 { + +#define currentEngine() Isolate::GetCurrent()->GetCurrentContext()->GetEngine() + +#define Q_D(obj) QQmlJS::VM::Value *d = reinterpret_cast<QQmlJS::VM::Value*>(obj) + +#define ValuePtr(obj) reinterpret_cast<QQmlJS::VM::Value*>(obj) +#define ConstValuePtr(obj) reinterpret_cast<const QQmlJS::VM::Value*>(obj) + +void *gcProtect(void *handle) +{ + Q_D(handle); + if (VM::Managed *m = d->asManaged()) { + currentEngine()->memoryManager->protect(m); + return currentEngine()->memoryManager; + } +} + +void gcProtect(void *memoryManager, void *handle) +{ + Q_D(handle); + if (VM::Managed *m = d->asManaged()) + if (memoryManager) + static_cast<VM::MemoryManager *>(memoryManager)->protect(m); +} + +void gcUnprotect(void *memoryManager, void *handle) +{ + Q_D(handle); + if (VM::Managed *m = d->asManaged()) + if (memoryManager) + static_cast<VM::MemoryManager *>(memoryManager)->unprotect(m); +} + +struct V8AccessorGetter: FunctionObject { + AccessorGetter getter; + Persistent<Value> data; + Persistent<String> name; + + V8AccessorGetter(ExecutionContext *scope, const Handle<String> &name, const AccessorGetter &getter, Handle<Value> data) + : FunctionObject(scope) + { + vtbl = &static_vtbl; + this->getter = getter; + this->data = Persistent<Value>::New(data); + this->name = Persistent<String>::New(name); + } + + using Object::construct; + + static VM::Value call(Managed *that, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc) + { + V8AccessorGetter *getter = static_cast<V8AccessorGetter*>(that); + AccessorInfo info(thisObject, getter->data); + VM::Value result = VM::Value::undefinedValue(); + try { + result = getter->getter(Local<String>::New(getter->name), info)->vmValue(); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(context); + } + return result; + } + +protected: + static const ManagedVTable static_vtbl; +}; + +DEFINE_MANAGED_VTABLE(V8AccessorGetter); + +struct V8AccessorSetter: FunctionObject { + AccessorSetter setter; + Persistent<Value> data; + Persistent<String> name; + + V8AccessorSetter(ExecutionContext *scope, const Handle<String> &name, const AccessorSetter &setter, Handle<Value> data) + : FunctionObject(scope) + { + vtbl = &static_vtbl; + this->setter = setter; + this->data = Persistent<Value>::New(data); + this->name = Persistent<String>::New(name); + } + + using Object::construct; + + static VM::Value call(Managed *that, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc) + { + if (!argc) + return VM::Value::undefinedValue(); + V8AccessorSetter *setter = static_cast<V8AccessorSetter*>(that); + AccessorInfo info(thisObject, setter->data); + try { + setter->setter(Local<String>::New(setter->name), Local<Value>::New(Value::fromVmValue(args[0])), info); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(context); + } + return VM::Value::undefinedValue(); + } + +protected: + static const ManagedVTable static_vtbl; +}; + +DEFINE_MANAGED_VTABLE(V8AccessorSetter); + +ScriptOrigin::ScriptOrigin(Handle<Value> resource_name, Handle<Integer> resource_line_offset, Handle<Integer> resource_column_offset) +{ + m_fileName = resource_name->ToString()->asQString(); + m_lineNumber = resource_line_offset->ToInt32()->Value(); + m_columnNumber = resource_column_offset->ToInt32()->Value(); +} + +Handle<Value> ScriptOrigin::ResourceName() const +{ + return Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_fileName)); +} + +Handle<Integer> ScriptOrigin::ResourceLineOffset() const +{ + return Integer::New(m_lineNumber); +} + +Handle<Integer> ScriptOrigin::ResourceColumnOffset() const +{ + return Integer::New(m_columnNumber); +} + + +Local<Script> Script::New(Handle<String> source, + ScriptOrigin* origin, + ScriptData* pre_data, + Handle<String> script_data, + CompileFlags flags) +{ + Script *s = new Script; + s->m_script = source->ToString()->asQString(); + if (origin) + s->m_origin = *origin; + s->m_flags = flags; + s->m_context = Handle<Context>(); + return Local<Script>::New(Handle<Script>(s)); +} + + +Local<Script> Script::New(Handle<String> source, + Handle<Value> file_name, + CompileFlags flags) +{ + ScriptOrigin origin(file_name); + return New(source, &origin, 0, Handle<String>(), flags); +} + +Local<Script> Script::Compile(Handle<String> source, ScriptOrigin *origin, ScriptData *pre_data, Handle<String> script_data, Script::CompileFlags flags) +{ + Script *s = new Script; + s->m_script = source->ToString()->asQString(); + if (origin) + s->m_origin = *origin; + s->m_flags = flags; + s->m_context = Context::GetCurrent(); + return Local<Script>::New(Handle<Script>(s)); +} + +Local<Script> Script::Compile(Handle<String> source, + Handle<Value> file_name, + Handle<String> script_data, + CompileFlags flags) +{ + ScriptOrigin origin(file_name); + return Compile(source, &origin, 0, script_data, flags); +} + +Local<Value> Script::Run() +{ + Handle<Context> context = m_context; + if (context.IsEmpty()) + context = Context::GetCurrent(); + ASSERT(context.get()); + VM::ExecutionEngine *engine = context->GetEngine(); + VM::ExecutionContext *ctx = engine->current; + + VM::Value result = VM::Value::undefinedValue(); + try { + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(engine->rootContext, m_origin.m_fileName, m_script, QQmlJS::Codegen::EvalCode, + /*strictMode =*/ false, /*inheritContext =*/ false); + if (!f) + __qmljs_throw(engine->current, VM::Value::fromObject(engine->newSyntaxErrorObject(engine->current, 0))); + + result = context->GetEngine()->run(f); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + + return Local<Value>::New(Value::fromVmValue(result)); +} + +Local<Value> Script::Run(Handle<Object> qml) +{ + Handle<Context> context = m_context; + if (context.IsEmpty()) + context = Context::GetCurrent(); + ASSERT(context.get()); + VM::ExecutionEngine *engine = context->GetEngine(); + VM::ExecutionContext *ctx = engine->current; + + VM::Value result = VM::Value::undefinedValue(); + + try { + + VM::EvalFunction *eval = new (engine->memoryManager) VM::EvalFunction(engine->rootContext, qml->vmValue().asObject()); + + VM::Value arg = VM::Value::fromString(engine->current, m_script); + + result = eval->evalCall(engine->current, VM::Value::undefinedValue(), &arg, 1, /*directCall*/ false); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + return Local<Value>::New(Value::fromVmValue(result)); +} + +Local<Value> Script::Id() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void Script::SetData(Handle<String> data) +{ + Q_UNIMPLEMENTED(); +} + + +Local<String> Message::Get() const +{ + return Local<String>::New(Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_message))); +} + +Handle<Value> Message::GetScriptResourceName() const +{ + return Value::fromVmValue(VM::Value::fromString(currentEngine()->current, m_resourceName)); +} + +int Message::GetLineNumber() const +{ + return m_lineNumber; +} + + +Local<StackFrame> StackTrace::GetFrame(uint32_t index) const +{ + if (index >= (uint)frames.size()) + return Local<StackFrame>(); + return frames.at(index); +} + +int StackTrace::GetFrameCount() const +{ + return frames.size(); +} + +Local<Array> StackTrace::AsArray() +{ + Q_UNIMPLEMENTED(); + return Local<Array>(); +} + +Local<StackTrace> StackTrace::CurrentStackTrace(int frame_limit, StackTrace::StackTraceOptions options) +{ + StackTrace *trace = new StackTrace; + VM::ExecutionEngine *engine = currentEngine(); + VM::ExecutionContext *current = engine->current; + while (current && frame_limit) { + if (CallContext *c = current->asCallContext()) { + StackFrame *frame = new StackFrame(Value::fromVmValue(VM::Value::fromString(engine->id_null)), + Value::fromVmValue(VM::Value::fromString(c->function->name)), + 0, 0); + trace->frames.append(frame); + --frame_limit; + } + current = current->parent; + } + + return Local<StackTrace>::New(Handle<StackTrace>(trace)); +} + + +int StackFrame::GetLineNumber() const +{ + return m_lineNumber; +} + +int StackFrame::GetColumn() const +{ + return m_columnNumber; +} + +Local<String> StackFrame::GetScriptName() const +{ + return Local<String>::New(m_scriptName); +} + +Local<String> StackFrame::GetScriptNameOrSourceURL() const +{ + return Local<String>::New(m_scriptName); +} + +Local<String> StackFrame::GetFunctionName() const +{ + return Local<String>::New(m_functionName); +} + +StackFrame::StackFrame(Handle<String> script, Handle<String> function, int line, int column) + : m_lineNumber(line) + , m_columnNumber(column) +{ + m_scriptName = Persistent<String>::New(script); + m_functionName = Persistent<String>::New(function); +} + + +bool Value::IsUndefined() const +{ + return ConstValuePtr(this)->isUndefined(); +} + +bool Value::IsNull() const { + return ConstValuePtr(this)->isNull(); +} + +bool Value::IsTrue() const +{ + return ConstValuePtr(this)->isBoolean() && ConstValuePtr(this)->booleanValue(); +} + +bool Value::IsFalse() const +{ + return !IsTrue(); +} + +bool Value::IsString() const +{ + return ConstValuePtr(this)->isString(); +} + +bool Value::IsFunction() const +{ + return ConstValuePtr(this)->asFunctionObject(); +} + +bool Value::IsArray() const +{ + return ConstValuePtr(this)->asArrayObject(); +} + +bool Value::IsObject() const +{ + return ConstValuePtr(this)->isObject(); +} + +bool Value::IsBoolean() const +{ + return ConstValuePtr(this)->isBoolean(); +} + +bool Value::IsNumber() const +{ + return ConstValuePtr(this)->isNumber(); +} + +bool Value::IsExternal() const +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +bool Value::IsInt32() const +{ + return ConstValuePtr(this)->isInteger(); +} + +bool Value::IsUint32() const +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +bool Value::IsDate() const +{ + return ConstValuePtr(this)->asDateObject(); +} + +bool Value::IsBooleanObject() const +{ + return ConstValuePtr(this)->asBooleanObject(); +} + +bool Value::IsNumberObject() const +{ + return ConstValuePtr(this)->asNumberObject(); +} + +bool Value::IsStringObject() const +{ + return ConstValuePtr(this)->asStringObject(); +} + +bool Value::IsRegExp() const +{ + return ConstValuePtr(this)->asRegExpObject(); +} + +bool Value::IsError() const +{ + return ConstValuePtr(this)->asErrorObject(); +} + +Local<Boolean> Value::ToBoolean() const +{ + return Local<Boolean>::New(Value::fromVmValue(VM::Value::fromBoolean(ConstValuePtr(this)->toBoolean()))); +} + +Local<Number> Value::ToNumber() const +{ + return Local<Number>::New(Value::fromVmValue(VM::Value::fromDouble(ConstValuePtr(this)->toNumber()))); +} + +Local<String> Value::ToString() const +{ + return Local<String>::New(Value::fromVmValue(VM::Value::fromString(ConstValuePtr(this)->toString(currentEngine()->current)))); +} + +Local<Object> Value::ToObject() const +{ + return Local<Object>::New(Value::fromVmValue(QQmlJS::VM::Value::fromObject(ConstValuePtr(this)->toObject(currentEngine()->current)))); +} + +Local<Integer> Value::ToInteger() const +{ + return Local<Integer>::New(Value::fromVmValue(QQmlJS::VM::Value::fromDouble(ConstValuePtr(this)->toInteger()))); +} + +Local<Uint32> Value::ToUint32() const +{ + return Local<Uint32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromUInt32(ConstValuePtr(this)->toUInt32()))); +} + +Local<Int32> Value::ToInt32() const +{ + return Local<Int32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromInt32(ConstValuePtr(this)->toInt32()))); +} + +Local<Uint32> Value::ToArrayIndex() const +{ + return Local<Uint32>::New(Value::fromVmValue(QQmlJS::VM::Value::fromUInt32(ConstValuePtr(this)->asArrayIndex()))); +} + +bool Value::BooleanValue() const +{ + return ConstValuePtr(this)->toBoolean(); +} + +double Value::NumberValue() const +{ + return ConstValuePtr(this)->asDouble(); +} + +int64_t Value::IntegerValue() const +{ + return (int64_t)ConstValuePtr(this)->toInteger(); +} + +uint32_t Value::Uint32Value() const +{ + return ConstValuePtr(this)->toUInt32(); +} + +int32_t Value::Int32Value() const +{ + return ConstValuePtr(this)->toInt32(); +} + +bool Value::Equals(Handle<Value> that) const +{ + return __qmljs_equal(*ConstValuePtr(this), *ConstValuePtr(&that)); +} + +bool Value::StrictEquals(Handle<Value> that) const +{ + return __qmljs_strict_equal(*ConstValuePtr(this), *ConstValuePtr(&that)); +} + +VM::Value Value::vmValue() const +{ + return *ConstValuePtr(this); +} + +Handle<Value> Value::fromVmValue(const VM::Value &vmValue) +{ + Handle<Value> res; + res.val = vmValue.val; + return res; +} + + +bool Boolean::Value() const +{ + return BooleanValue(); +} + +Handle<Boolean> Boolean::New(bool value) +{ + return Value::fromVmValue(VM::Value::fromBoolean(value)); +} + + +int String::Length() const +{ + return asVMString()->toQString().length(); +} + +uint32_t String::Hash() const +{ + return asVMString()->hashValue(); +} + + +String::CompleteHashData String::CompleteHash() const +{ + VM::String *s = asVMString(); + CompleteHashData data; + data.hash = s->hashValue(); + data.length = s->toQString().length(); + data.symbol_id = s->identifier; + return data; +} + +uint32_t String::ComputeHash(uint16_t *string, int length) +{ + return VM::String::createHashValue(reinterpret_cast<const QChar *>(string), length); +} + +uint32_t String::ComputeHash(char *string, int length) +{ + // ### unefficient + QString s = QString::fromLatin1((char *)string, length); + return VM::String::createHashValue(s.constData(), s.length()); +} + +bool String::Equals(uint16_t *str, int length) +{ + return asQString() == QString(reinterpret_cast<QChar*>(str), length); +} + +bool String::Equals(char *str, int length) +{ + return asQString() == QString::fromLatin1(str, length); +} + +uint16_t String::GetCharacter(int index) +{ + return asQString().at(index).unicode(); +} + +int String::Write(uint16_t *buffer, int start, int length, int options) const +{ + if (length < 0) + length = asQString().length(); + if (length == 0) + return 0; + if (asQString().length() < length) + length = asQString().length(); + // ### do we use options? + memcpy(buffer + start, asQString().constData(), length*sizeof(QChar)); + return length; +} + +v8::Local<String> String::Empty() +{ + return Local<String>::New(v8::Value::fromVmValue(VM::Value::fromString(currentEngine()->current, QString()))); +} + +bool String::IsExternal() const +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +String::ExternalStringResource *String::GetExternalStringResource() const +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +String *String::Cast(v8::Value *obj) +{ + return static_cast<String *>(obj); +} + + +Local<String> String::New(const char *data, int length) +{ + QQmlJS::VM::Value v = QQmlJS::VM::Value::fromString(currentEngine()->current, QString::fromLatin1(data, length)); + return Local<String>::New(v8::Value::fromVmValue(v)); +} + +Local<String> String::New(const uint16_t *data, int length) +{ + QQmlJS::VM::Value v = QQmlJS::VM::Value::fromString(currentEngine()->current, QString((const QChar *)data, length)); + return Local<String>::New(v8::Value::fromVmValue(v)); +} + +Local<String> String::NewSymbol(const char *data, int length) +{ + QString str = QString::fromLatin1(data, length); + VM::String *vmString = currentEngine()->newIdentifier(str); + return New(vmString); +} + +Local<String> String::New(VM::String *s) +{ + return Local<String>::New(v8::Value::fromVmValue(VM::Value::fromString(s))); +} + +Local<String> String::NewExternal(String::ExternalStringResource *resource) +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +QString String::asQString() const +{ + return asVMString()->toQString(); +} + +VM::String *String::asVMString() const +{ + const VM::Value *v = ConstValuePtr(this); + ASSERT(v->isString()); + return v->stringValue(); +} + +String::AsciiValue::AsciiValue(Handle<v8::Value> obj) +{ + str = obj->ToString()->asQString().toLatin1(); +} + +String::Value::Value(Handle<v8::Value> obj) +{ + str = obj->ToString()->asQString(); +} + + +double Number::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return v->asDouble(); +} + +Local<Number> Number::New(double value) +{ + return Local<Number>::New(Value::fromVmValue(VM::Value::fromDouble(value))); +} + +Number *Number::Cast(v8::Value *obj) +{ + return static_cast<Number *>(obj); +} + +Local<Integer> Integer::New(int32_t value) +{ + return Local<Integer>::New(Value::fromVmValue(VM::Value::fromInt32(value))); +} + +Local<Integer> Integer::NewFromUnsigned(uint32_t value) +{ + return Local<Integer>::New(Value::fromVmValue(VM::Value::fromUInt32(value))); +} + +Local<Integer> Integer::New(int32_t value, Isolate *) +{ + return New(value); +} + +Local<Integer> Integer::NewFromUnsigned(uint32_t value, Isolate *) +{ + return NewFromUnsigned(value); +} + +int64_t Integer::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return (int64_t)v->asDouble(); +} + +Integer *Integer::Cast(v8::Value *obj) +{ + return static_cast<Integer *>(obj); +} + +int32_t Int32::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isInteger()); + return v->int_32; +} + +uint32_t Uint32::Value() const +{ + const VM::Value *v = ConstValuePtr(this); + assert(v->isNumber()); + return v->toUInt32(); +} + + +struct ExternalResourceWrapper : public QQmlJS::VM::Object::ExternalResource +{ + ExternalResourceWrapper(v8::Object::ExternalResource *wrapped) + { + this->wrapped = wrapped; + } + + virtual ~ExternalResourceWrapper() + { + wrapped->Dispose(); + } + + v8::Object::ExternalResource *wrapped; +}; + + +bool Object::Set(Handle<Value> key, Handle<Value> value, PropertyAttribute attribs) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + bool result = true; + try { + o->put(ctx, ValuePtr(&key)->toString(ctx), *ValuePtr(&value)); + // ### attribs + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + result = false; + } + return result; +} + +bool Object::Set(uint32_t index, Handle<Value> value) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + bool result = true; + try { + o->putIndexed(ctx, index, *ValuePtr(&value)); + // ### attribs + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + result = false; + } + return result; +} + +Local<Value> Object::Get(Handle<Value> key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + QQmlJS::VM::Value prop = VM::Value::undefinedValue(); + try { + prop = o->get(ctx, ValuePtr(&key)->toString(ctx)); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + return Local<Value>::New(Value::fromVmValue(prop)); +} + +Local<Value> Object::Get(uint32_t key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + QQmlJS::VM::Value prop = VM::Value::undefinedValue(); + try { + prop = o->getIndexed(ctx, key); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + return Local<Value>::New(Value::fromVmValue(prop)); +} + +bool Object::Has(Handle<String> key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + return o->__hasProperty__(ValuePtr(&key)->asString()); +} + +bool Object::Delete(Handle<String> key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + bool result = false; + ExecutionContext *ctx = currentEngine()->current; + try { + result = o->deleteProperty(ctx, ValuePtr(&key)->asString()); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + return result; +} + +bool Object::Has(uint32_t index) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return false; + return o->__hasProperty__(index); +} + +bool Object::Delete(uint32_t index) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + ExecutionContext *ctx = currentEngine()->current; + bool result = false; + try { + result = o->deleteIndexedProperty(ctx, index); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(ctx); + } + return result; +} + +bool Object::SetAccessor(Handle<String> name, AccessorGetter getter, AccessorSetter setter, Handle<Value> data, AccessControl settings, PropertyAttribute attribute) +{ + VM::ExecutionEngine *engine = currentEngine(); + + VM::FunctionObject *wrappedGetter = 0; + if (getter) { + wrappedGetter = new (engine->memoryManager) V8AccessorGetter(engine->rootContext, name, getter, data); + } + VM::FunctionObject *wrappedSetter = 0; + if (setter) { + wrappedSetter = new (engine->memoryManager) V8AccessorSetter(engine->rootContext, name, setter, data); + } + + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + PropertyAttributes attrs = Attr_Accessor; + attrs.setConfigurable(!(attribute & DontDelete)); + attrs.setEnumerable(!(attribute & DontEnum)); + VM::Property *pd = o->insertMember(name->asVMString(), attrs); + pd->setGetter(wrappedGetter); + pd->setSetter(wrappedSetter); + return true; +} + +Local<Array> Object::GetPropertyNames() +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + + VM::ArrayObject *array = currentEngine()->newArrayObject(currentEngine()->current)->asArrayObject(); + ObjectIterator it(currentEngine()->current, o, ObjectIterator::WithProtoChain|ObjectIterator::EnumberableOnly); + while (1) { + VM::Value v = it.nextPropertyNameAsString(); + if (v.isNull()) + break; + array->push_back(v); + } + return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(array))); +} + +Local<Array> Object::GetOwnPropertyNames() +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + VM::Value arg = VM::Value::fromObject(o); + ArrayObject *array = currentEngine()->newArrayObject(currentEngine()->current)->asArrayObject(); + ObjectIterator it(currentEngine()->current, o, ObjectIterator::EnumberableOnly); + while (1) { + VM::Value v = it.nextPropertyNameAsString(); + if (v.isNull()) + break; + array->push_back(v); + } + return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(array))); +} + +Local<Value> Object::GetPrototype() +{ + Local<Value> result; + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + return Local<Value>::New(Value::fromVmValue(QQmlJS::VM::Value::fromObject(o->prototype))); +} + +bool Object::SetPrototype(Handle<Value> prototype) +{ + QQmlJS::VM::Object *p = ConstValuePtr(&prototype)->asObject(); + if (!p) + return false; + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + o->prototype = p; + return true; +} + +Local<Value> Object::GetInternalField(int index) +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void Object::SetInternalField(int index, Handle<Value> value) +{ + Q_UNIMPLEMENTED(); +} + +void Object::SetExternalResource(Object::ExternalResource *resource) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o) + return; + o->externalResource = new ExternalResourceWrapper(resource); +} + +Object::ExternalResource *Object::GetExternalResource() +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o || !o->externalResource) + return 0; + return static_cast<ExternalResourceWrapper*>(o->externalResource)->wrapped; +} + +bool Object::HasOwnProperty(Handle<String> key) +{ + QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); + assert(o); + QQmlJS::VM::ExecutionContext *ctx = currentEngine()->current; + return o->__getOwnProperty__(ValuePtr(&key)->toString(ctx)); +} + +int Object::GetIdentityHash() +{ + return (quintptr)ConstValuePtr(this)->asObject() >> 2; +} + +bool Object::SetHiddenValue(Handle<String> key, Handle<Value> value) +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +Local<Value> Object::GetHiddenValue(Handle<String> key) +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +Local<Object> Object::Clone() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +bool Object::IsCallable() +{ + return ConstValuePtr(this)->asFunctionObject(); +} + +Local<Value> Object::CallAsFunction(Handle<Object> recv, int argc, Handle<Value> argv[]) +{ + VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + if (!f) + return Local<Value>(); + VM::Value retval = f->call(currentEngine()->current, recv->vmValue(), + reinterpret_cast<QQmlJS::VM::Value*>(argv), + argc); + return Local<Value>::New(Value::fromVmValue(retval)); +} + +Local<Value> Object::CallAsConstructor(int argc, Handle<Value> argv[]) +{ + VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + if (!f) + return Local<Value>(); + VM::Value retval = f->construct(currentEngine()->current, + reinterpret_cast<QQmlJS::VM::Value*>(argv), + argc); + return Local<Value>::New(Value::fromVmValue(retval)); +} + +Local<Object> Object::New() +{ + VM::Object *o = currentEngine()->newObject(); + return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Object *Object::Cast(Value *obj) +{ + return static_cast<Object *>(obj); +} + + +uint32_t Array::Length() const +{ + VM::ArrayObject *a = ConstValuePtr(this)->asArrayObject(); + if (!a) + return 0; + return a->arrayLength(); +} + +Local<Array> Array::New(int length) +{ + VM::ArrayObject *a = currentEngine()->newArrayObject(currentEngine()->current); + if (length < 0x1000) + a->arrayReserve(length); + + return Local<Array>::New(Value::fromVmValue(VM::Value::fromObject(a))); +} + +Array *Array::Cast(Value *obj) +{ + return static_cast<Array *>(obj); +} + + +Local<Object> Function::NewInstance() const +{ + VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + assert(f); + VM::ExecutionContext *context = currentEngine()->current; + QQmlJS::VM::Value result = VM::Value::undefinedValue(); + try { + result = f->construct(context, 0, 0); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(context); + } + return Local<Object>::New(Value::fromVmValue(result)); +} + +Local<Object> Function::NewInstance(int argc, Handle<Value> argv[]) const +{ + VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + assert(f); + VM::ExecutionContext *context = currentEngine()->current; + QQmlJS::VM::Value result = VM::Value::undefinedValue(); + try { + result = f->construct(context, reinterpret_cast<QQmlJS::VM::Value*>(argv), argc); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(context); + } + return Local<Object>::New(Value::fromVmValue(result)); +} + +Local<Value> Function::Call(Handle<Object> thisObj, int argc, Handle<Value> argv[]) +{ + QQmlJS::VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + if (!f) + return Local<Value>(); + VM::ExecutionContext *context = currentEngine()->current; + QQmlJS::VM::Value result = VM::Value::undefinedValue(); + try { + result = f->call(context, *ConstValuePtr(&thisObj), + reinterpret_cast<QQmlJS::VM::Value*>(argv), argc); + } catch (VM::Exception &e) { + Isolate::GetCurrent()->setException(e.value()); + e.accept(context); + } + return Local<Value>::New(Value::fromVmValue(result)); +} + +Handle<Value> Function::GetName() const +{ + QQmlJS::VM::FunctionObject *f = ConstValuePtr(this)->asFunctionObject(); + if (!f) + return Handle<Value>(); + return Value::fromVmValue(VM::Value::fromString(f->name)); +} + +ScriptOrigin Function::GetScriptOrigin() const +{ + Q_UNIMPLEMENTED(); + return ScriptOrigin(); +} + +Function *Function::Cast(Value *obj) +{ + return static_cast<Function *>(obj); +} + + +Local<Value> Date::New(double time) +{ + VM::Object *o = currentEngine()->newDateObject(VM::Value::fromDouble(time)); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +double Date::NumberValue() const +{ + DateObject *d = ConstValuePtr(this)->asDateObject(); + assert(d); + return d->value.doubleValue(); +} + +Date *Date::Cast(Value *obj) +{ + return static_cast<Date *>(obj); +} + +void Date::DateTimeConfigurationChangeNotification() +{ + Q_UNIMPLEMENTED(); +} + + +Local<Value> NumberObject::New(double value) +{ + VM::Object *o = currentEngine()->newNumberObject(VM::Value::fromDouble(value)); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +double NumberObject::NumberValue() const +{ + VM::NumberObject *n = ConstValuePtr(this)->asNumberObject(); + assert(n); + return n->value.doubleValue(); +} + +NumberObject *NumberObject::Cast(Value *obj) +{ + return static_cast<NumberObject *>(obj); +} + +Local<Value> BooleanObject::New(bool value) +{ + VM::Object *o = currentEngine()->newBooleanObject(VM::Value::fromBoolean(value)); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +bool BooleanObject::BooleanValue() const +{ + VM::BooleanObject *b = ConstValuePtr(this)->asBooleanObject(); + assert(b); + return b->value.booleanValue(); +} + +BooleanObject *BooleanObject::Cast(Value *obj) +{ + return static_cast<BooleanObject *>(obj); +} + +Local<Value> StringObject::New(Handle<String> value) +{ + VM::Object *o = currentEngine()->newStringObject(currentEngine()->current, VM::Value::fromString(value->vmValue().asString())); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<String> StringObject::StringValue() const +{ + VM::StringObject *s = ConstValuePtr(this)->asStringObject(); + assert(s); + return Local<String>::New(Value::fromVmValue(s->value)); +} + +StringObject *StringObject::Cast(Value *obj) +{ + return static_cast<StringObject *>(obj); +} + +Local<RegExp> RegExp::New(Handle<String> pattern, RegExp::Flags flags) +{ + int f = 0; + if (flags & kGlobal) + f |= V4IR::RegExp::RegExp_Global; + if (flags & kIgnoreCase) + f |= V4IR::RegExp::RegExp_IgnoreCase; + if (flags & kMultiline) + f |= V4IR::RegExp::RegExp_Multiline; + VM::Object *o = currentEngine()->newRegExpObject(pattern->asQString(), f); + return Local<RegExp>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<String> RegExp::GetSource() const +{ + RegExpObject *re = ConstValuePtr(this)->asRegExpObject(); + assert(re); + return Local<String>::New(Value::fromVmValue(VM::Value::fromString(currentEngine()->current, re->value->pattern()))); +} + +RegExp::Flags RegExp::GetFlags() const +{ + RegExpObject *re = ConstValuePtr(this)->asRegExpObject(); + assert(re); + + int f = 0; + if (re->global) + f |= kGlobal; + if (re->value->ignoreCase()) + f |= kIgnoreCase; + if (re->value->multiLine()) + f |= kMultiline; + + return (RegExp::Flags)f; +} + +RegExp *RegExp::Cast(Value *obj) +{ + return static_cast<RegExp *>(obj); +} + +struct VoidStarWrapper : public VM::Object::ExternalResource +{ + void *data; +}; + +Local<Value> External::Wrap(void *data) +{ + return New(data); +} + +void *External::Unwrap(Handle<v8::Value> obj) +{ + return obj.As<External>()->Value(); +} + +Local<External> External::New(void *value) +{ + VM::Object *o = currentEngine()->newObject(); + VoidStarWrapper *wrapper = new VoidStarWrapper; + wrapper->data = value; + o->externalResource = wrapper; + return Local<v8::External>::New(v8::Value::fromVmValue(VM::Value::fromObject(o))); +} + +External *External::Cast(v8::Value *obj) +{ + return static_cast<External *>(obj); +} + +void *External::Value() const +{ + VM::Object *o = ConstValuePtr(this)->asObject(); + if (!o || !o->externalResource) + return 0; + return static_cast<VoidStarWrapper*>(o->externalResource)->data; +} + + +void Template::Set(Handle<String> name, Handle<Value> value, PropertyAttribute attributes) +{ + Property p; + p.name = Persistent<String>::New(name); + p.value = Persistent<Value>::New(value); + p.attributes = attributes; + m_properties << p; +} + +void Template::Set(const char *name, Handle<Value> value) +{ + Set(String::New(name), value); +} + + +Arguments::Arguments(const VM::Value *args, int argc, const VM::Value &thisObject, bool isConstructor, const Persistent<Value> &data) +{ + for (int i = 0; i < argc; ++i) + m_args << Persistent<Value>::New(Value::fromVmValue(args[i])); + m_thisObject = Persistent<Object>::New(Value::fromVmValue(thisObject)); + m_isConstructor = isConstructor; + m_data = Persistent<Value>::New(data); +} + +int Arguments::Length() const +{ + return m_args.size(); +} + +Local<Value> Arguments::operator [](int i) const +{ + return Local<Value>::New(m_args.at(i)); +} + +Local<Object> Arguments::This() const +{ + return Local<Object>::New(m_thisObject); +} + +Local<Object> Arguments::Holder() const +{ + // ### FIXME. + return Local<Object>::New(m_thisObject); +} + +bool Arguments::IsConstructCall() const +{ + return m_isConstructor; +} + +Local<Value> Arguments::Data() const +{ + return Local<Value>::New(m_data); +} + +Isolate *Arguments::GetIsolate() const +{ + return Isolate::GetCurrent(); +} + + +AccessorInfo::AccessorInfo(const VM::Value &thisObject, const Persistent<Value> &data) +{ + m_this = Persistent<Object>::New(Value::fromVmValue(thisObject)); + m_data = data; +} + +Isolate *AccessorInfo::GetIsolate() const +{ + return Isolate::GetCurrent(); +} + +Local<Value> AccessorInfo::Data() const +{ + return Local<Value>::New(m_data); +} + +Local<Object> AccessorInfo::This() const +{ + return Local<Object>::New(m_this); +} + +Local<Object> AccessorInfo::Holder() const +{ + // ### FIXME + return Local<Object>::New(m_this); +} + +template <typename BaseClass> +class V4V8Object : public BaseClass +{ +public: + V4V8Object(VM::ExecutionEngine *engine, ObjectTemplate *tmpl) + : BaseClass(engine->rootContext) + { + this->vtbl = &static_vtbl; + m_template = Persistent<ObjectTemplate>(tmpl); + if (m_template.IsEmpty()) + m_template = Persistent<ObjectTemplate>::New(ObjectTemplate::New()); + + foreach (const ObjectTemplate::Accessor &acc, m_template->m_accessors) { + PropertyAttributes attrs = Attr_Accessor; + attrs.setConfigurable(!(acc.attribute & DontDelete)); + attrs.setEnumerable(!(acc.attribute & DontEnum)); + VM::Property *pd = this->insertMember(acc.name->asVMString(), attrs); + *pd = VM::Property::fromAccessor(acc.getter->vmValue().asFunctionObject(), + acc.setter->vmValue().asFunctionObject()); + } + + initProperties(m_template.get()); + } + + void initProperties(Template *tmpl) + { + foreach (const Template::Property &p, tmpl->m_properties) { + PropertyAttributes attrs = Attr_Data; + attrs.setConfigurable(!(p.attributes & DontDelete)); + attrs.setEnumerable(!(p.attributes & DontEnum)); + attrs.setWritable(!(p.attributes & ReadOnly)); + VM::Property *pd = this->insertMember(p.name->asVMString(), attrs); + *pd = VM::Property::fromValue(p.value->vmValue()); + } + } + + Persistent<ObjectTemplate> m_template; + +protected: + AccessorInfo namedAccessorInfo() + { + // ### thisObject? + return AccessorInfo(VM::Value::fromObject(this), m_template->m_namedPropertyData); + } + AccessorInfo fallbackAccessorInfo() + { + // ### thisObject? + return AccessorInfo(VM::Value::fromObject(this), m_template->m_fallbackPropertyData); + } + AccessorInfo indexedAccessorInfo() + { + // ### thisObject? + return AccessorInfo(VM::Value::fromObject(this), m_template->m_namedPropertyData); + } + + static const ManagedVTable static_vtbl; + + static VM::Value get(VM::Managed *m, ExecutionContext *ctx, VM::String *name, bool *hasProperty) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_namedPropertyGetter) { + Handle<Value> result = that->m_template->m_namedPropertyGetter(String::New(name), that->namedAccessorInfo()); + if (!result.IsEmpty()) { + if (hasProperty) + *hasProperty = true; + return result->vmValue(); + } + } + + bool hasProp = false; + VM::Value result = BaseClass::get(m, ctx, name, &hasProp); + + if (!hasProp && that->m_template->m_fallbackPropertyGetter) { + Handle<Value> fallbackResult = that->m_template->m_fallbackPropertyGetter(String::New(name), that->fallbackAccessorInfo()); + if (!fallbackResult.IsEmpty()) { + if (hasProperty) + *hasProperty = true; + return fallbackResult->vmValue(); + } + } + + if (hasProperty) + *hasProperty = hasProp; + return result; + } + + static VM::Value getIndexed(VM::Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_indexedPropertyGetter) { + Handle<Value> result = that->m_template->m_indexedPropertyGetter(index, that->indexedAccessorInfo()); + if (!result.IsEmpty()) { + if (hasProperty) + *hasProperty = true; + return result->vmValue(); + } + } + return BaseClass::getIndexed(m, ctx, index, hasProperty); + } + + static void put(VM::Managed *m, ExecutionContext *ctx, VM::String *name, const VM::Value &value) + { + Local<Value> v8Value = Local<Value>::New(Value::fromVmValue(value)); + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_namedPropertySetter) { + Handle<Value> result = that->m_template->m_namedPropertySetter(String::New(name), v8Value, that->namedAccessorInfo()); + if (!result.IsEmpty()) + return; + } + PropertyAttributes attrs; + Property *pd = that->__getOwnProperty__(name, &attrs); + if (pd) + that->putValue(ctx, pd, attrs, value); + else if (that->m_template->m_fallbackPropertySetter) + that->m_template->m_fallbackPropertySetter(String::New(name), v8Value, that->fallbackAccessorInfo()); + else + BaseClass::put(m, ctx, name, value); + } + + static void putIndexed(VM::Managed *m, ExecutionContext *ctx, uint index, const VM::Value &value) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_indexedPropertySetter) { + Handle<Value> result = that->m_template->m_indexedPropertySetter(index, Local<Value>::New(Value::fromVmValue(value)), that->indexedAccessorInfo()); + if (!result.IsEmpty()) + return; + } + BaseClass::putIndexed(m, ctx, index, value); + } + + static PropertyAttributes propertyAttributesToFlags(const Handle<Value> &attr) + { + PropertyAttributes flags; + int intAttr = attr->ToInt32()->Value(); + flags.setWritable(!(intAttr & ReadOnly)); + flags.setEnumerable(!(intAttr & DontEnum)); + flags.setConfigurable(!(intAttr & DontDelete)); + return flags; + } + + static PropertyAttributes query(VM::Managed *m, ExecutionContext *ctx, VM::String *name) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_namedPropertyQuery) { + Handle<Value> result = that->m_template->m_namedPropertyQuery(String::New(name), that->namedAccessorInfo()); + if (!result.IsEmpty()) + return propertyAttributesToFlags(result); + } + PropertyAttributes flags = BaseClass::query(m, ctx, name); + if (flags.type() == PropertyAttributes::Generic && that->m_template->m_fallbackPropertySetter) { + Handle<Value> result = that->m_template->m_fallbackPropertyQuery(String::New(name), that->fallbackAccessorInfo()); + if (!result.IsEmpty()) + return propertyAttributesToFlags(result); + } + + return flags; + } + + static PropertyAttributes queryIndexed(VM::Managed *m, ExecutionContext *ctx, uint index) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_indexedPropertyQuery) { + Handle<Value> result = that->m_template->m_indexedPropertyQuery(index, that->indexedAccessorInfo()); + if (!result.IsEmpty()) + return propertyAttributesToFlags(result); + } + + return BaseClass::queryIndexed(m, ctx, index); + } + + static bool deleteProperty(VM::Managed *m, ExecutionContext *ctx, VM::String *name) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_namedPropertyDeleter) { + Handle<Boolean> result = that->m_template->m_namedPropertyDeleter(String::New(name), that->namedAccessorInfo()); + if (!result.IsEmpty()) + return result->Value(); + } + + bool result = BaseClass::deleteProperty(m, ctx, name); + + if (that->m_template->m_fallbackPropertyDeleter) { + Handle<Boolean> interceptResult = that->m_template->m_fallbackPropertyDeleter(String::New(name), that->fallbackAccessorInfo()); + if (!interceptResult.IsEmpty()) + result = interceptResult->Value(); + } + + return result; + } + + static bool deleteIndexedProperty(VM::Managed *m, ExecutionContext *ctx, uint index) + { + V4V8Object *that = static_cast<V4V8Object*>(m); + if (that->m_template->m_indexedPropertyDeleter) { + Handle<Boolean> result = that->m_template->m_indexedPropertyDeleter(index, that->indexedAccessorInfo()); + if (!result.IsEmpty()) + return result->Value(); + } + return BaseClass::deleteIndexedProperty(m, ctx, index); + } +}; + +template<> +DEFINE_MANAGED_VTABLE(V4V8Object<VM::Object>); +template<> +DEFINE_MANAGED_VTABLE(V4V8Object<VM::FunctionObject>); +template<> +DEFINE_MANAGED_VTABLE(V4V8Object<VM::FunctionPrototype>); + +struct V4V8Function : public V4V8Object<VM::FunctionObject> +{ + V4V8Function(VM::ExecutionEngine *engine, FunctionTemplate *functionTemplate) + : V4V8Object<VM::FunctionObject>(engine, 0) + { + vtbl = &static_vtbl; + m_functionTemplate = Persistent<FunctionTemplate>(functionTemplate); + initProperties(m_functionTemplate.get()); + } + +protected: + static const ManagedVTable static_vtbl; + + static VM::Value call(VM::Managed *m, ExecutionContext *context, const VM::Value &thisObject, VM::Value *args, int argc) + { + V4V8Function *that = static_cast<V4V8Function*>(m); + Arguments arguments(args, argc, thisObject, false, that->m_functionTemplate->m_data); + VM::Value result = VM::Value::undefinedValue(); + if (that->m_functionTemplate->m_callback) + result = that->m_functionTemplate->m_callback(arguments)->vmValue(); + return result; + } + + static VM::Value construct(VM::Managed *m, ExecutionContext *context, VM::Value *args, int argc) + { + V4V8Function *that = static_cast<V4V8Function*>(m); + Arguments arguments(args, argc, VM::Value::undefinedValue(), true, that->m_functionTemplate->m_data); + + VM::Object *obj = that->m_functionTemplate->m_instanceTemplate->NewInstance()->vmValue().asObject(); + VM::Value proto = that->Managed::get(context, context->engine->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + VM::Value result = VM::Value::undefinedValue(); + if (that->m_functionTemplate->m_callback) + result = that->m_functionTemplate->m_callback(arguments)->vmValue(); + if (result.isObject()) + return result; + return VM::Value::fromObject(obj); + + } + + Persistent<FunctionTemplate> m_functionTemplate; +}; + +DEFINE_MANAGED_VTABLE(V4V8Function); + +FunctionTemplate::FunctionTemplate(InvocationCallback callback, Handle<Value> data) + : m_callback(callback) +{ + m_instanceTemplate = Local<ObjectTemplate>(); + m_prototypeTemplate = Local<ObjectTemplate>(); + m_data = Persistent<Value>::New(data); +} + +Local<FunctionTemplate> FunctionTemplate::New(InvocationCallback callback, Handle<Value> data) +{ + FunctionTemplate *ft = new FunctionTemplate(callback, data); + return Local<FunctionTemplate>::New(Handle<FunctionTemplate>(ft)); +} + +Local<Function> FunctionTemplate::GetFunction() +{ + VM::ExecutionEngine *engine = currentEngine(); + VM::Object *o = new (engine->memoryManager) V4V8Function(engine, this); + VM::Object *proto = new (engine->memoryManager) V4V8Object<VM::FunctionPrototype>(engine, m_prototypeTemplate.get()); + o->put(engine->current, engine->id_prototype, VM::Value::fromObject(proto)); + return Local<Function>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<ObjectTemplate> FunctionTemplate::InstanceTemplate() +{ + if (m_instanceTemplate.IsEmpty()) + m_instanceTemplate = ObjectTemplate::New(); + return m_instanceTemplate; +} + +Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() +{ + if (m_prototypeTemplate.IsEmpty()) + m_prototypeTemplate = ObjectTemplate::New(); + return m_prototypeTemplate; +} + + +Local<ObjectTemplate> ObjectTemplate::New() +{ + ObjectTemplate *ot = new ObjectTemplate; + return Local<ObjectTemplate>::New(Handle<ObjectTemplate>(ot)); +} + +Local<Object> ObjectTemplate::NewInstance() +{ + VM::ExecutionEngine *engine = currentEngine(); + VM::Object *o = new (engine->memoryManager) V4V8Object<VM::Object>(engine, this); + o->prototype = engine->objectPrototype; + o->externalComparison = m_useUserComparison; + + return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +void ObjectTemplate::SetAccessor(Handle<String> name, AccessorGetter getter, AccessorSetter setter, Handle<Value> data, AccessControl settings, PropertyAttribute attribute) +{ + VM::ExecutionEngine *engine = currentEngine(); + + Accessor a; + if (getter) { + VM::FunctionObject *wrappedGetter = new (engine->memoryManager) V8AccessorGetter(engine->rootContext, name, getter, data); + a.getter = Persistent<Value>::New(Value::fromVmValue(VM::Value::fromObject(wrappedGetter))); + } + if (setter) { + VM::FunctionObject *wrappedSetter = new (engine->memoryManager) V8AccessorSetter(engine->rootContext, name, setter, data); + a.setter = Persistent<Value>::New(Value::fromVmValue(VM::Value::fromObject(wrappedSetter))); + } + a.attribute = attribute; + a.name = Persistent<String>::New(name); + m_accessors << a; +} + +void ObjectTemplate::SetNamedPropertyHandler(NamedPropertyGetter getter, NamedPropertySetter setter, NamedPropertyQuery query, NamedPropertyDeleter deleter, NamedPropertyEnumerator enumerator, Handle<Value> data) +{ + m_namedPropertyGetter = getter; + m_namedPropertySetter = setter; + m_namedPropertyQuery = query; + m_namedPropertyDeleter = deleter; + m_namedPropertyEnumerator = enumerator; + m_namedPropertyData = Persistent<Value>::New(data); +} + +void ObjectTemplate::SetFallbackPropertyHandler(NamedPropertyGetter getter, NamedPropertySetter setter, NamedPropertyQuery query, NamedPropertyDeleter deleter, NamedPropertyEnumerator enumerator, Handle<Value> data) +{ + m_fallbackPropertyGetter = getter; + m_fallbackPropertySetter = setter; + m_fallbackPropertyQuery = query; + m_fallbackPropertyDeleter = deleter; + m_fallbackPropertyEnumerator = enumerator; + m_fallbackPropertyData = Persistent<Value>::New(data); +} + +void ObjectTemplate::SetIndexedPropertyHandler(IndexedPropertyGetter getter, IndexedPropertySetter setter, IndexedPropertyQuery query, IndexedPropertyDeleter deleter, IndexedPropertyEnumerator enumerator, Handle<Value> data) +{ + m_indexedPropertyGetter = getter; + m_indexedPropertySetter = setter; + m_indexedPropertyQuery = query; + m_indexedPropertyDeleter = deleter; + m_indexedPropertyEnumerator = enumerator; + m_indexedPropertyData = Persistent<Value>::New(data); +} + +int ObjectTemplate::InternalFieldCount() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void ObjectTemplate::SetInternalFieldCount(int value) +{ + Q_UNIMPLEMENTED(); +} + +bool ObjectTemplate::HasExternalResource() +{ + // we always reserve the space for the external resource + return true; +} + +void ObjectTemplate::SetHasExternalResource(bool value) +{ + // no need for this, we always reserve the space for the external resource + Q_UNUSED(value); +} + +void ObjectTemplate::MarkAsUseUserObjectComparison() +{ + m_useUserComparison = true; +} + +ObjectTemplate::ObjectTemplate() +{ + m_namedPropertyGetter = 0; + m_namedPropertySetter = 0; + m_namedPropertyQuery = 0; + m_namedPropertyDeleter = 0; + m_namedPropertyEnumerator = 0; + + m_fallbackPropertyGetter = 0; + m_fallbackPropertySetter = 0; + m_fallbackPropertyQuery = 0; + m_fallbackPropertyDeleter = 0; + m_fallbackPropertyEnumerator = 0; + + m_indexedPropertyGetter = 0; + m_indexedPropertySetter = 0; + m_indexedPropertyQuery = 0; + m_indexedPropertyDeleter = 0; + m_indexedPropertyEnumerator = 0; + + m_useUserComparison = false; +} + +Handle<Primitive> Undefined() +{ + Handle<Primitive> val; + val.val = VM::Value::undefinedValue().val; + return val; +} + +Handle<Primitive> Null() +{ + Handle<Primitive> val; + val.val = VM::Value::nullValue().val; + return val; +} + +Handle<Boolean> True() +{ + Handle<Primitive> val; + val.val = VM::Value::fromBoolean(true).val; + return val; +} + +Handle<Boolean> False() +{ + Handle<Primitive> val; + val.val = VM::Value::fromBoolean(false).val; + return val; +} + + +Handle<Value> ThrowException(Handle<Value> exception) +{ + __qmljs_throw(currentEngine()->current, exception->vmValue()); + return Handle<Value>(); +} + + +Local<Value> Exception::ReferenceError(Handle<String> message) +{ + Q_UNUSED(message); + VM::Object *o = currentEngine()->newReferenceErrorObject(currentEngine()->current, message->ToString()->asQString()); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<Value> Exception::SyntaxError(Handle<String> message) +{ + Q_UNUSED(message); + VM::Object *o = currentEngine()->newSyntaxErrorObject(currentEngine()->current, 0); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<Value> Exception::TypeError(Handle<String> message) +{ + Q_UNUSED(message); + VM::Object *o = currentEngine()->newTypeErrorObject(currentEngine()->current, message->ToString()->asQString()); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + +Local<Value> Exception::Error(Handle<String> message) +{ + Q_UNUSED(message); + VM::Object *o = currentEngine()->newErrorObject(VM::Value::fromString(currentEngine()->current, message->ToString()->asQString())); + return Local<Value>::New(Value::fromVmValue(VM::Value::fromObject(o))); +} + + +static QThreadStorage<Isolate*> currentIsolate; + +Isolate::Isolate() + : m_lastIsolate(0) + , tryCatch(0) +{ +} + +Isolate::~Isolate() +{ +} + +Isolate *Isolate::New() +{ + assert(!"Isolate::New()"); + Q_UNREACHABLE(); +} + +void Isolate::Enter() +{ + m_lastIsolate = currentIsolate.localData(); + currentIsolate.localData() = this; +} + +void Isolate::Exit() +{ + currentIsolate.localData() = m_lastIsolate; + m_lastIsolate = 0; +} + +void Isolate::Dispose() +{ + Q_UNIMPLEMENTED(); +} + +void Isolate::SetData(void *data) +{ + Q_UNIMPLEMENTED(); +} + +void *Isolate::GetData() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void Isolate::setException(const VM::Value &ex) +{ + if (tryCatch) { + tryCatch->hasCaughtException = true; + tryCatch->exception = Local<Value>::New(Value::fromVmValue(ex)); + } +} + +Isolate *Isolate::GetCurrent() +{ + if (!currentIsolate.hasLocalData()) + currentIsolate.setLocalData(new Isolate); + return currentIsolate.localData(); +} + + +void V8::SetFlagsFromString(const char *, int) +{ + // we can safely ignore these +} + +static UserObjectComparisonCallback userObjectComparisonCallback = 0; + +static bool v8ExternalResourceComparison(const VM::Value &a, const VM::Value &b) +{ + if (!userObjectComparisonCallback) + return false; + Local<Object> la = Local<Object>::New(Value::fromVmValue(a)); + Local<Object> lb = Local<Object>::New(Value::fromVmValue(b)); + return userObjectComparisonCallback(la, lb); +} + +void V8::SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback callback) +{ + userObjectComparisonCallback = callback; + currentEngine()->externalResourceComparison = v8ExternalResourceComparison; +} + +void V8::AddGCPrologueCallback(GCPrologueCallback, GCType) +{ + // not required currently as we don't have weak Persistent references. + // not having them will lead to some leaks in QQmlVMEMetaObejct, but shouldn't matter otherwise +} + +void V8::RemoveGCPrologueCallback(GCPrologueCallback) +{ + assert(!"RemoveGCPrologueCallback();"); +} + +void V8::AddImplicitReferences(Persistent<Object> parent, Persistent<Value> *children, size_t length) +{ + // not required currently as we don't have weak Persistent references. + // not having them will lead to some leaks in QQmlVMEMetaObejct, but shouldn't matter otherwise + assert(!"AddImplicitReferences();"); +} + +bool V8::Initialize() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +bool V8::Dispose() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +bool V8::IdleNotification(int hint) +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void V8::LowMemoryNotification() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + + +TryCatch::TryCatch() +{ + Isolate *i = Isolate::GetCurrent(); + + hasCaughtException = false; + parent = i->tryCatch; + i->tryCatch = this; +} + +TryCatch::~TryCatch() +{ + Isolate *i = Isolate::GetCurrent(); + i->tryCatch = parent; +} + +bool TryCatch::HasCaught() const +{ + return hasCaughtException; +} + +Handle<Value> TryCatch::ReThrow() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +Local<Value> TryCatch::Exception() const +{ + return exception; +} + +Local<Message> TryCatch::Message() const +{ + Q_UNIMPLEMENTED(); + return Local<v8::Message>::New(Handle<v8::Message>(new v8::Message(QString(), QString(), 0))); +} + +void TryCatch::Reset() +{ + hasCaughtException = false; +} + + + +struct Context::Private +{ + Private() + { + engine.reset(new QQmlJS::VM::ExecutionEngine); + } + + QScopedPointer<QQmlJS::VM::ExecutionEngine> engine; +}; + +Context::Context() + : m_lastContext(0) + , d(new Private) +{ +} + +Context::~Context() +{ + delete d; +} + +Persistent<Context> Context::New(ExtensionConfiguration *extensions, Handle<ObjectTemplate> global_template, Handle<Value> global_object) +{ + Context *result = new Context; + return Persistent<Context>::New(Handle<Context>(result)); +} + +Local<Object> Context::Global() +{ + return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(d->engine->globalObject))); +} + +Local<Context> Context::GetCurrent() +{ + return Context::Adopt(Isolate::GetCurrent()->m_contextStack.top()); +} + +Local<Context> Context::GetCalling() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +Local<Object> Context::GetCallingQmlGlobal() +{ + VM::ExecutionEngine *engine = GetCurrent()->GetEngine(); + VM::ExecutionContext *ctx = engine->current; + while (ctx && ctx->outer != engine->rootContext) + ctx = ctx->outer; + + assert(ctx); + if (!ctx->type == ExecutionContext::Type_QmlContext) + return Local<Object>(); + + return Local<Object>::New(Value::fromVmValue(VM::Value::fromObject(static_cast<CallContext *>(ctx)->activation))); +} + +Local<Value> Context::GetCallingScriptData() +{ + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); +} + +void Context::Enter() +{ + Isolate* iso = Isolate::GetCurrent(); + iso->m_contextStack.push(this); +} + +void Context::Exit() +{ + Isolate::GetCurrent()->m_contextStack.pop(); +} + + +QQmlJS::VM::ExecutionEngine *Context::GetEngine() +{ + return d->engine.data(); +} + + +} diff --git a/src/qml/qml/v4vm/qv4v8.h b/src/qml/qml/v4vm/qv4v8.h new file mode 100644 index 0000000000..bcd6a7fef3 --- /dev/null +++ b/src/qml/qml/v4vm/qv4v8.h @@ -0,0 +1,2581 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/** \mainpage V8 API Reference Guide + * + * V8 is Google's open source JavaScript engine. + * + * This set of documents provides reference material generated from the + * V8 header file, include/v8.h. + * + * For other documentation see http://code.google.com/apis/v8/ + */ + +#ifndef V8_H_ +#define V8_H_ + +#include "qv4global.h" +#include "qv4string.h" +#include <QStack> +#include <QSharedData> + +namespace QQmlJS { +namespace VM { +struct Value; +struct String; +struct ExecutionEngine; +struct Object; +class MemoryManager; +} +} + +#include <stdint.h> + +#define V8EXPORT Q_V4_EXPORT + +/** + * The v8 JavaScript engine. + */ +namespace v8 { + +class Context; +class String; +class StringObject; +class Value; +class Utils; +class Number; +class NumberObject; +class Object; +class Array; +class Int32; +class Uint32; +class External; +class Primitive; +class Boolean; +class BooleanObject; +class Integer; +class Function; +class Date; +class ImplementationUtilities; +class Signature; +class AccessorSignature; +template <class T> struct Handle; +template <class T> class Local; +template <class T> class Persistent; +class FunctionTemplate; +class ObjectTemplate; +class Data; +class AccessorInfo; +class StackTrace; +class StackFrame; +class Isolate; +class TryCatch; + +V8EXPORT void *gcProtect(void *handle); +V8EXPORT void gcProtect(void *memoryManager, void *handle); +V8EXPORT void gcUnprotect(void *memoryManager, void *handle); + +// --- Weak Handles --- + +/** + * A weak reference callback function. + * + * This callback should either explicitly invoke Dispose on |object| if + * V8 wrapper is not needed anymore, or 'revive' it by invocation of MakeWeak. + * + * \param object the weak global object to be reclaimed by the garbage collector + * \param parameter the value passed in when making the weak global object + */ +typedef void (*WeakReferenceCallback)(Persistent<Value> object, + void* parameter); + + +// --- Handles --- + +#define TYPE_CHECK(T, S) \ + while (false) { \ + *(static_cast<T* volatile*>(0)) = static_cast<S*>(0); \ + } + +/** + * An object reference managed by the v8 garbage collector. + * + * All objects returned from v8 have to be tracked by the garbage + * collector so that it knows that the objects are still alive. Also, + * because the garbage collector may move objects, it is unsafe to + * point directly to an object. Instead, all objects are stored in + * handles which are known by the garbage collector and updated + * whenever an object moves. Handles should always be passed by value + * (except in cases like out-parameters) and they should never be + * allocated on the heap. + * + * There are two types of handles: local and persistent handles. + * Local handles are light-weight and transient and typically used in + * local operations. They are managed by HandleScopes. Persistent + * handles can be used when storing objects across several independent + * operations and have to be explicitly deallocated when they're no + * longer used. + * + * It is safe to extract the object stored in the handle by + * dereferencing the handle (for instance, to extract the Object* from + * a Handle<Object>); the value will still be governed by a handle + * behind the scenes and the same rules apply to these values as to + * their handles. + */ + +template <typename T> +struct Handle; + +template <typename T> +struct HandleOperations +{ + static void init(Handle<T> *handle) + { +#if QT_POINTER_SIZE == 8 + handle->val = quint64(Handle<T>::_Null_Type) << Handle<T>::Tag_Shift; +#else + handle->tag = Handle<T>::_Null_Type; + handle->int_32 = 0; +#endif + } + + static void ref(Handle<T> *) + { + } + + static void deref(Handle<T> *) + { + } + + static void *protect(Handle<T> *handle) + { + return gcProtect(handle); + } + + static void protect(void *memoryManager, Handle<T> *handle) + { + gcProtect(memoryManager, handle); + } + + static void unProtect(void *memoryManager, Handle<T> *handle) + { + gcUnprotect(memoryManager, handle); + } + + static bool isEmpty(const Handle<T> *handle) + { + return handle->tag == Handle<T>::_Null_Type; + } + + static T *get(const Handle<T> *handle) + { + return const_cast<T*>(reinterpret_cast<const T*>(handle)); + } +}; + +#define DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Type) \ + template <> \ + struct HandleOperations<Type> \ + { \ + static void init(Handle<Type> *handle) \ + { \ + handle->object = 0; \ + } \ + \ + static void ref(Handle<Type> *handle) \ + { \ + if (handle->object) \ + handle->object->ref.ref(); \ + } \ + \ + static void deref(Handle<Type> *handle) \ + { \ + if (handle->object && !handle->object->ref.deref()) { \ + delete handle->object; \ + handle->object = 0; \ + } \ + } \ + static void *protect(Handle<Type> *) { return 0; } \ + static void protect(void *, Handle<Type> *) {} \ + static void unProtect(void *, Handle<Type> *) {} \ + static bool isEmpty(const Handle<Type> *handle) \ + { \ + return handle->object == 0; \ + } \ + static Type *get(const Handle<Type> *handle) \ + { \ + return handle->object; \ + } \ + \ + }; + +template <typename T> +struct Handle { + Handle() + { + HandleOperations<T>::init(this); + } + template <typename Other> + Handle(const Handle<Other> &that) + : val(that.val) + { + HandleOperations<T>::ref(this); + } + + explicit Handle(T *obj) + { + object = obj; + HandleOperations<T>::ref(this); + } + + Handle(const Handle<T> &other) + : val(other.val) + { + HandleOperations<T>::ref(this); + } + Handle<T> &operator=(const Handle<T> &other) + { + if (this == &other) + return *this; + HandleOperations<T>::deref(this); + this->val = other.val; + HandleOperations<T>::ref(this); + return *this; + } + ~Handle() + { + HandleOperations<T>::deref(this); + } + + bool IsEmpty() const { return HandleOperations<T>::isEmpty(this); } + + T *operator->() const { return HandleOperations<T>::get(this); } + + T *get() const { return HandleOperations<T>::get(this); } + + template <typename Source> + static Handle<T> Cast(Handle<Source> that) + { + return that.template As<T>(); + } + + template <typename Target> + Handle<Target> As() + { + return Handle<Target>(*this); + } + + void Clear() + { + val = 0; + } + + template <class S> inline bool operator==(Handle<S> that) const { + return val == that.val; + } + template <class S> inline bool operator!=(Handle<S> that) const { + return val != that.val; + } + + enum Masks { + NotDouble_Mask = 0xfffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + Tag_Shift = 32 + }; + + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + union { + T *object; + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if QT_POINTER_SIZE == 4 + T *o; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; +}; + + +/** + * A light-weight stack-allocated object handle. All operations + * that return objects from within v8 return them in local handles. They + * are created within HandleScopes, and all local handles allocated within a + * handle scope are destroyed when the handle scope is destroyed. Hence it + * is not necessary to explicitly deallocate local handles. + */ +template <class T> class Local : public Handle<T> { + public: + Local() {} + template <class S> Local(Local<S> that) + : Handle<T>(Handle<T>::Cast(that)) { + /** + * This check fails when trying to convert between incompatible + * handles. For example, converting from a Handle<String> to a + * Handle<Number>. + */ + TYPE_CHECK(T, S); + } + template <class S> Local(S* that) : Handle<T>(that) { } + template <class S> static Local<T> Cast(Local<S> that) { +#ifdef V8_ENABLE_CHECKS + // If we're going to perform the type check then we have to check + // that the handle isn't empty before doing the checked cast. + if (that.IsEmpty()) return Local<T>(); +#endif + return Local<T>::New(Handle<T>::Cast(that)); + } + + template <class S> Local<S> As() { + return Local<S>::Cast(*this); + } + + /** Create a local handle for the content of another handle. + * The referee is kept alive by the local handle even when + * the original handle is destroyed/disposed. + */ + static Local<T> New(Handle<T> that) + { + Local<T> result; + result.Handle<T>::operator =(that); + return result; + } +}; + + +/** + * An object reference that is independent of any handle scope. Where + * a Local handle only lives as long as the HandleScope in which it was + * allocated, a Persistent handle remains valid until it is explicitly + * disposed. + * + * A persistent handle contains a reference to a storage cell within + * the v8 engine which holds an object value and which is updated by + * the garbage collector whenever the object is moved. A new storage + * cell can be created using Persistent::New and existing handles can + * be disposed using Persistent::Dispose. Since persistent handles + * are passed by value you may have many persistent handle objects + * that point to the same storage cell. For instance, if you pass a + * persistent handle as an argument to a function you will not get two + * different storage cells but rather two references to the same + * storage cell. + */ +template <class T> class Persistent : public Handle<T> { + public: + /** + * Creates an empty persistent handle that doesn't point to any + * storage cell. + */ + Persistent() {} + ~Persistent() { + HandleOperations<T>::unProtect(m_memoryManager, this); + } + + Persistent(const Persistent &other) + : Handle<T>(other) + , m_memoryManager(other.m_memoryManager) + { + HandleOperations<T>::protect(m_memoryManager, this); + } + + Persistent &operator =(const Persistent &other) + { + if (&other == this) + return *this; + HandleOperations<T>::unProtect(m_memoryManager, this); + Handle<T>::operator =(other); + m_memoryManager = other.m_memoryManager; + HandleOperations<T>::protect(m_memoryManager, this); + return *this; + } + + /** + * Creates a persistent handle for the same storage cell as the + * specified handle. This constructor allows you to pass persistent + * handles as arguments by value and to assign between persistent + * handles. However, attempting to assign between incompatible + * persistent handles, for instance from a Persistent<String> to a + * Persistent<Number> will cause a compile-time error. Assigning + * between compatible persistent handles, for instance assigning a + * Persistent<String> to a variable declared as Persistent<Value>, + * is allowed as String is a subclass of Value. + */ + template <class S> Persistent(Persistent<S> that) + : Handle<T>(Handle<T>::Cast(that)) { + m_memoryManager = that.m_memoryManager; + HandleOperations<T>::protect(m_memoryManager, this); + } + + template <class S> Persistent(S* that) : Handle<T>(that) + { + m_memoryManager = HandleOperations<T>::protect(this); + } + + /** + * "Casts" a plain handle which is known to be a persistent handle + * to a persistent handle. + */ + template <class S> explicit Persistent(Handle<S> that) + : Handle<T>(*that) + { + m_memoryManager = HandleOperations<T>::protect(this); + } + + template <class S> static Persistent<T> Cast(Persistent<S> that) { + return Persistent<T>(T::Cast(*that)); + } + + template <class S> Persistent<S> As() { + return Persistent<S>::Cast(*this); + } + + /** + * Creates a new persistent handle for an existing local or + * persistent handle. + */ + static Persistent<T> New(Handle<T> that) + { + Persistent<T> result; + result.Handle<T>::operator =(that); + result.m_memoryManager = HandleOperations<T>::protect(&result); + return result; + } + + /** + * Releases the storage cell referenced by this persistent handle. + * Does not remove the reference to the cell from any handles. + * This handle's reference, and any other references to the storage + * cell remain and IsEmpty will still return false. + */ + void Dispose() { + HandleOperations<T>::unProtect(m_memoryManager, this); + m_memoryManager = 0; + HandleOperations<T>::deref(this); + HandleOperations<T>::init(this); + } + + void Dispose(Isolate*) { + Dispose(); + } + + /** + * Make the reference to this object weak. When only weak handles + * refer to the object, the garbage collector will perform a + * callback to the given V8::WeakReferenceCallback function, passing + * it the object reference and the given parameters. + */ + void MakeWeak(void* parameters, WeakReferenceCallback callback); +public: + void *m_memoryManager; +}; + + + /** + * A stack-allocated class that governs a number of local handles. + * After a handle scope has been created, all local handles will be + * allocated within that handle scope until either the handle scope is + * deleted or another handle scope is created. If there is already a + * handle scope and a new one is created, all allocations will take + * place in the new handle scope until it is deleted. After that, + * new handles will again be allocated in the original handle scope. + * + * After the handle scope of a local handle has been deleted the + * garbage collector will no longer track the object stored in the + * handle and may deallocate it. The behavior of accessing a handle + * for which the handle scope has been deleted is undefined. + */ +class V8EXPORT HandleScope { + public: + HandleScope() {} + + ~HandleScope() {} + + /** + * Closes the handle scope and returns the value as a handle in the + * previous scope, which is the new current scope after the call. + */ + template <class T> Local<T> Close(Handle<T> value) { return Local<T>::New(value); } +}; + + +// --- Special objects --- + + +/** + * The superclass of values and API object templates. + */ +class V8EXPORT Data : public QSharedData { +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Data) + +/** + * The origin, within a file, of a script. + */ +class V8EXPORT ScriptOrigin { +public: + ScriptOrigin() : m_lineNumber(0), m_columnNumber(0) {} + + ScriptOrigin( + Handle<Value> resource_name, + Handle<Integer> resource_line_offset = Handle<Integer>(), + Handle<Integer> resource_column_offset = Handle<Integer>()); + Handle<Value> ResourceName() const; + Handle<Integer> ResourceLineOffset() const; + Handle<Integer> ResourceColumnOffset() const; +private: + QString m_fileName; + int m_lineNumber, m_columnNumber; + friend class Script; +}; + +class ScriptData; + +/** + * A compiled JavaScript script. + */ +class V8EXPORT Script : public QSharedData { + public: + enum CompileFlags { + Default = 0x00, + QmlMode = 0x01, + NativeMode = 0x02 + }; + + /** + * Compiles the specified script (context-independent). + * + * \param source Script source code. + * \param origin Script origin, owned by caller, no references are kept + * when New() returns + * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() + * using pre_data speeds compilation if it's done multiple times. + * Owned by caller, no references are kept when New() returns. + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but allows data to be + * available to compile event handlers. + * \return Compiled script object (context independent; when run it + * will use the currently entered context). + */ + static Local<Script> New(Handle<String> source, + ScriptOrigin* origin = NULL, + ScriptData* pre_data = NULL, + Handle<String> script_data = Handle<String>(), + CompileFlags = Default); + + /** + * Compiles the specified script using the specified file name + * object (typically a string) as the script's origin. + * + * \param source Script source code. + * \param file_name file name object (typically a string) to be used + * as the script's origin. + * \return Compiled script object (context independent; when run it + * will use the currently entered context). + */ + static Local<Script> New(Handle<String> source, + Handle<Value> file_name, + CompileFlags = Default); + + /** + * Compiles the specified script (bound to current context). + * + * \param source Script source code. + * \param origin Script origin, owned by caller, no references are kept + * when Compile() returns + * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() + * using pre_data speeds compilation if it's done multiple times. + * Owned by caller, no references are kept when Compile() returns. + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but makes data available + * earlier (i.e. to compile event handlers). + * \return Compiled script object, bound to the context that was active + * when this function was called. When run it will always use this + * context. + */ + static Local<Script> Compile(Handle<String> source, + ScriptOrigin* origin = NULL, + ScriptData* pre_data = NULL, + Handle<String> script_data = Handle<String>(), + CompileFlags = Default); + + /** + * Compiles the specified script using the specified file name + * object (typically a string) as the script's origin. + * + * \param source Script source code. + * \param file_name File name to use as script's origin + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but makes data available + * earlier (i.e. to compile event handlers). + * \return Compiled script object, bound to the context that was active + * when this function was called. When run it will always use this + * context. + */ + static Local<Script> Compile(Handle<String> source, + Handle<Value> file_name, + Handle<String> script_data = Handle<String>(), + CompileFlags = Default); + + /** + * Runs the script returning the resulting value. If the script is + * context independent (created using ::New) it will be run in the + * currently entered context. If it is context specific (created + * using ::Compile) it will be run in the context in which it was + * compiled. + */ + Local<Value> Run(); + Local<Value> Run(Handle<Object> qml); + + /** + * Returns the script id value. + */ + Local<Value> Id(); + + /** + * Associate an additional data object with the script. This is mainly used + * with the debugger as this data object is only available through the + * debugger API. + */ + void SetData(Handle<String> data); + +private: + QString m_script; + ScriptOrigin m_origin; + CompileFlags m_flags; + Handle<Context> m_context; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Script) + +/** + * An error message. + */ +class V8EXPORT Message : public QSharedData { + public: + Message(const QString &message, const QString &resourceName, int lineNumber) + : m_message(message), m_resourceName(resourceName), m_lineNumber(lineNumber) {} + + Local<String> Get() const; + /** + * Returns the resource name for the script from where the function causing + * the error originates. + */ + Handle<Value> GetScriptResourceName() const; + + /** + * Returns the number, 1-based, of the line where the error occurred. + */ + int GetLineNumber() const; + +private: + QString m_message; + QString m_resourceName; + int m_lineNumber; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Message) + +/** + * Representation of a JavaScript stack trace. The information collected is a + * snapshot of the execution stack and the information remains valid after + * execution continues. + */ +class V8EXPORT StackTrace : public QSharedData +{ + public: + /** + * Flags that determine what information is placed captured for each + * StackFrame when grabbing the current stack trace. + */ + enum StackTraceOptions { + kLineNumber = 1, + kColumnOffset = 1 << 1 | kLineNumber, + kScriptName = 1 << 2, + kFunctionName = 1 << 3, + kIsEval = 1 << 4, + kIsConstructor = 1 << 5, + kScriptNameOrSourceURL = 1 << 6, + kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName, + kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL + }; + + /** + * Returns a StackFrame at a particular index. + */ + Local<StackFrame> GetFrame(uint32_t index) const; + + /** + * Returns the number of StackFrames. + */ + int GetFrameCount() const; + + /** + * Returns StackTrace as a v8::Array that contains StackFrame objects. + */ + Local<Array> AsArray(); + + /** + * Grab a snapshot of the current JavaScript execution stack. + * + * \param frame_limit The maximum number of stack frames we want to capture. + * \param options Enumerates the set of things we will capture for each + * StackFrame. + */ + static Local<StackTrace> CurrentStackTrace( + int frame_limit, + StackTraceOptions options = kOverview); + + private: + QVector<Local<StackFrame> > frames; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackTrace) + + +/** + * A single JavaScript stack frame. + */ +class V8EXPORT StackFrame : public QSharedData { + public: + /** + * Returns the number, 1-based, of the line for the associate function call. + * This method will return Message::kNoLineNumberInfo if it is unable to + * retrieve the line number, or if kLineNumber was not passed as an option + * when capturing the StackTrace. + */ + int GetLineNumber() const; + + /** + * Returns the 1-based column offset on the line for the associated function + * call. + * This method will return Message::kNoColumnInfo if it is unable to retrieve + * the column number, or if kColumnOffset was not passed as an option when + * capturing the StackTrace. + */ + int GetColumn() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame. + */ + Local<String> GetScriptName() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame or sourceURL value if the script name + * is undefined and its source ends with //@ sourceURL=... string. + */ + Local<String> GetScriptNameOrSourceURL() const; + + /** + * Returns the name of the function associated with this stack frame. + */ + Local<String> GetFunctionName() const; + +private: + friend class StackTrace; + StackFrame(Handle<String> script, Handle<String> function, int line, int column); + int m_lineNumber; + int m_columnNumber; + Persistent<String> m_scriptName; + Persistent<String> m_functionName; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(StackFrame) + +// --- Value --- + + +/** + * The superclass of all JavaScript values and objects. + */ +class V8EXPORT Value { + public: + /** + * Returns true if this value is the undefined value. See ECMA-262 + * 4.3.10. + */ + bool IsUndefined() const; + + /** + * Returns true if this value is the null value. See ECMA-262 + * 4.3.11. + */ + bool IsNull() const; + + /** + * Returns true if this value is true. + */ + bool IsTrue() const; + + /** + * Returns true if this value is false. + */ + bool IsFalse() const; + + /** + * Returns true if this value is an instance of the String type. + * See ECMA-262 8.4. + */ + bool IsString() const; + + /** + * Returns true if this value is a function. + */ + bool IsFunction() const; + + /** + * Returns true if this value is an array. + */ + bool IsArray() const; + + /** + * Returns true if this value is an object. + */ + bool IsObject() const; + + /** + * Returns true if this value is boolean. + */ + bool IsBoolean() const; + + /** + * Returns true if this value is a number. + */ + bool IsNumber() const; + + /** + * Returns true if this value is external. + */ + bool IsExternal() const; + + /** + * Returns true if this value is a 32-bit signed integer. + */ + bool IsInt32() const; + + /** + * Returns true if this value is a 32-bit unsigned integer. + */ + bool IsUint32() const; + + /** + * Returns true if this value is a Date. + */ + bool IsDate() const; + + /** + * Returns true if this value is a Boolean object. + */ + bool IsBooleanObject() const; + + /** + * Returns true if this value is a Number object. + */ + bool IsNumberObject() const; + + /** + * Returns true if this value is a String object. + */ + bool IsStringObject() const; + + /** + * Returns true if this value is a RegExp. + */ + bool IsRegExp() const; + + /** + * Returns true if this value is an Error. + */ + bool IsError() const; + + Local<Boolean> ToBoolean() const; + Local<Number> ToNumber() const; + Local<String> ToString() const; + Local<Object> ToObject() const; + Local<Integer> ToInteger() const; + Local<Uint32> ToUint32() const; + Local<Int32> ToInt32() const; + + /** + * Attempts to convert a string to an array index. + * Returns an empty handle if the conversion fails. + */ + Local<Uint32> ToArrayIndex() const; + + bool BooleanValue() const; + double NumberValue() const; + int64_t IntegerValue() const; + uint32_t Uint32Value() const; + int32_t Int32Value() const; + + /** JS == */ + bool Equals(Handle<Value> that) const; + bool StrictEquals(Handle<Value> that) const; + + static Handle<Value> NewFromInternalValue(quint64 val) + { + Handle<Value> res; + res.val = val; + return res; + } + + QQmlJS::VM::Value vmValue() const; + static Handle<Value> fromVmValue(const QQmlJS::VM::Value &vmValue); + +}; + + +/** + * The superclass of primitive values. See ECMA-262 4.3.2. + */ +class V8EXPORT Primitive : public Value { }; + + +/** + * A primitive boolean value (ECMA-262, 4.3.14). Either the true + * or false value. + */ +class V8EXPORT Boolean : public Primitive { + public: + bool Value() const; + static Handle<Boolean> New(bool value); +}; + + +/** + * A JavaScript string value (ECMA-262, 4.3.17). + */ +class V8EXPORT String : public Primitive { + public: + /** + * Returns the number of characters in this string. + */ + int Length() const; + + + /** + * Returns the hash of this string. + */ + uint32_t Hash() const; + + struct CompleteHashData { + CompleteHashData() : length(0), hash(0), symbol_id(0) {} + int length; + uint32_t hash; + uint32_t symbol_id; + }; + + /** + * Returns the "complete" hash of the string. This is + * all the information about the string needed to implement + * a very efficient hash keyed on the string. + * + * The members of CompleteHashData are: + * length: The length of the string. Equivalent to Length() + * hash: The hash of the string. Equivalent to Hash() + * symbol_id: If the string is a sequential symbol, the symbol + * id, otherwise 0. If the symbol ids of two strings are + * the same (and non-zero) the two strings are identical. + * If the symbol ids are different the strings may still be + * identical, but an Equals() check must be performed. + */ + CompleteHashData CompleteHash() const; + + /** + * Compute a hash value for the passed UTF16 string + * data. + */ + static uint32_t ComputeHash(uint16_t *string, int length); + static uint32_t ComputeHash(char *string, int length); + + /** + * Returns true if this string is equal to the external + * string data provided. + */ + bool Equals(uint16_t *string, int length); + bool Equals(char *string, int length); + bool Equals(Handle<Value> that) const { + return v8::Value::Equals(that); + } + + /** + * Write the contents of the string to an external buffer. + * If no arguments are given, expects the buffer to be large + * enough to hold the entire string and NULL terminator. Copies + * the contents of the string and the NULL terminator into the + * buffer. + * + * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop + * before the end of the buffer. + * + * Copies up to length characters into the output buffer. + * Only null-terminates if there is enough space in the buffer. + * + * \param buffer The buffer into which the string will be copied. + * \param start The starting position within the string at which + * copying begins. + * \param length The number of characters to copy from the string. For + * WriteUtf8 the number of bytes in the buffer. + * \param nchars_ref The number of characters written, can be NULL. + * \param options Various options that might affect performance of this or + * subsequent operations. + * \return The number of characters copied to the buffer excluding the null + * terminator. For WriteUtf8: The number of bytes copied to the buffer + * including the null terminator (if written). + */ + enum WriteOptions { + NO_OPTIONS = 0, + HINT_MANY_WRITES_EXPECTED = 1, + NO_NULL_TERMINATION = 2, + PRESERVE_ASCII_NULL = 4 + }; + + uint16_t GetCharacter(int index); + + // 16-bit character codes. + int Write(uint16_t* buffer, + int start = 0, + int length = -1, + int options = NO_OPTIONS) const; + + /** + * A zero length string. + */ + static v8::Local<v8::String> Empty(); + static v8::Local<v8::String> Empty(Isolate* isolate); + + /** + * Returns true if the string is external + */ + bool IsExternal() const; + + class V8EXPORT ExternalStringResourceBase { // NOLINT + public: + virtual ~ExternalStringResourceBase() {} + + protected: + ExternalStringResourceBase() {} + + /** + * Internally V8 will call this Dispose method when the external string + * resource is no longer needed. The default implementation will use the + * delete operator. This method can be overridden in subclasses to + * control how allocated external string resources are disposed. + */ + virtual void Dispose() { delete this; } + + }; + + /** + * An ExternalStringResource is a wrapper around a two-byte string + * buffer that resides outside V8's heap. Implement an + * ExternalStringResource to manage the life cycle of the underlying + * buffer. Note that the string data must be immutable. + */ + class V8EXPORT ExternalStringResource + : public ExternalStringResourceBase { + public: + /** + * Override the destructor to manage the life cycle of the underlying + * buffer. + */ + virtual ~ExternalStringResource() {} + + /** + * The string data from the underlying buffer. + */ + virtual const uint16_t* data() const = 0; + + /** + * The length of the string. That is, the number of two-byte characters. + */ + virtual size_t length() const = 0; + + protected: + ExternalStringResource() {} + }; + + /** + * Get the ExternalStringResource for an external string. Returns + * NULL if IsExternal() doesn't return true. + */ + ExternalStringResource* GetExternalStringResource() const; + + static String* Cast(v8::Value* obj); + + /** + * Allocates a new string from either UTF-8 encoded or ASCII data. + * The second parameter 'length' gives the buffer length. + * If the data is UTF-8 encoded, the caller must + * be careful to supply the length parameter. + * If it is not given, the function calls + * 'strlen' to determine the buffer length, it might be + * wrong if 'data' contains a null character. + */ + static Local<String> New(const char* data, int length = -1); + + /** Allocates a new string from 16-bit character codes.*/ + static Local<String> New(const uint16_t* data, int length = -1); + + /** Creates a symbol. Returns one if it exists already.*/ + static Local<String> NewSymbol(const char* data, int length = -1); + + static Local<String> New(QQmlJS::VM::String *s); + + /** + * Creates a new external string using the data defined in the given + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed by calling its Dispose method. The caller of + * this function should not otherwise delete or modify the resource. Neither + * should the underlying buffer be deallocated or modified except through the + * destructor of the external string resource. + */ + static Local<String> NewExternal(ExternalStringResource* resource); + + /** + * Converts an object to an ASCII string. + * Useful if you want to print the object. + * If conversion to a string fails (eg. due to an exception in the toString() + * method of the object) then the length() method returns 0 and the * operator + * returns NULL. + */ + class V8EXPORT AsciiValue { + public: + explicit AsciiValue(Handle<v8::Value> obj); + ~AsciiValue() {} + char* operator*() { return str.data(); } + const char* operator*() const { return str.constData(); } + int length() const { return str.length(); } + private: + QByteArray str; + + // Disallow copying and assigning. + AsciiValue(const AsciiValue&); + void operator=(const AsciiValue&); + }; + + /** + * Converts an object to a two-byte string. + * If conversion to a string fails (eg. due to an exception in the toString() + * method of the object) then the length() method returns 0 and the * operator + * returns NULL. + */ + class V8EXPORT Value { + public: + explicit Value(Handle<v8::Value> obj); + ~Value() {} + uint16_t* operator*() { return (uint16_t *)str.data(); } + const uint16_t* operator*() const { return str.utf16(); } + int length() const { return str.length(); } + private: + QString str; + + // Disallow copying and assigning. + Value(const Value&); + void operator=(const Value&); + }; + + QString asQString() const; + QQmlJS::VM::String *asVMString() const; +}; + + +/** + * A JavaScript number value (ECMA-262, 4.3.20) + */ +class V8EXPORT Number : public Primitive { + public: + double Value() const; + static Local<Number> New(double value); + static Number* Cast(v8::Value* obj); +}; + + +/** + * A JavaScript value representing a signed integer. + */ +class V8EXPORT Integer : public Number { + public: + static Local<Integer> New(int32_t value); + static Local<Integer> NewFromUnsigned(uint32_t value); + static Local<Integer> New(int32_t value, Isolate*); + static Local<Integer> NewFromUnsigned(uint32_t value, Isolate*); + int64_t Value() const; + static Integer* Cast(v8::Value* obj); +}; + + +/** + * A JavaScript value representing a 32-bit signed integer. + */ +class V8EXPORT Int32 : public Integer { + public: + int32_t Value() const; + private: + Int32(); +}; + + +/** + * A JavaScript value representing a 32-bit unsigned integer. + */ +class V8EXPORT Uint32 : public Integer { + public: + uint32_t Value() const; + private: + Uint32(); +}; + + +enum PropertyAttribute { + None = 0, + ReadOnly = 1 << 0, + DontEnum = 1 << 1, + DontDelete = 1 << 2 +}; + +/** + * Accessor[Getter|Setter] are used as callback functions when + * setting|getting a particular property. See Object and ObjectTemplate's + * method SetAccessor. + */ +typedef Handle<Value> (*AccessorGetter)(Local<String> property, + const AccessorInfo& info); + + +typedef void (*AccessorSetter)(Local<String> property, + Local<Value> value, + const AccessorInfo& info); + + +/** + * Access control specifications. + * + * Some accessors should be accessible across contexts. These + * accessors have an explicit access control parameter which specifies + * the kind of cross-context access that should be allowed. + * + * Additionally, for security, accessors can prohibit overwriting by + * accessors defined in JavaScript. For objects that have such + * accessors either locally or in their prototype chain it is not + * possible to overwrite the accessor by using __defineGetter__ or + * __defineSetter__ from JavaScript code. + */ +enum AccessControl { + DEFAULT = 0, + ALL_CAN_READ = 1, + ALL_CAN_WRITE = 1 << 1, + PROHIBITS_OVERWRITING = 1 << 2 +}; + + +/** + * A JavaScript object (ECMA-262, 4.3.3) + */ +class V8EXPORT Object : public Value { + public: + bool Set(Handle<Value> key, + Handle<Value> value, + PropertyAttribute attribs = None); + + bool Set(uint32_t index, + Handle<Value> value); + + Local<Value> Get(Handle<Value> key); + + Local<Value> Get(uint32_t index); + + // TODO(1245389): Replace the type-specific versions of these + // functions with generic ones that accept a Handle<Value> key. + bool Has(Handle<String> key); + + bool Delete(Handle<String> key); + + bool Has(uint32_t index); + + bool Delete(uint32_t index); + + bool SetAccessor(Handle<String> name, + AccessorGetter getter, + AccessorSetter setter = 0, + Handle<Value> data = Handle<Value>(), + AccessControl settings = DEFAULT, + PropertyAttribute attribute = None); + + /** + * Returns an array containing the names of the enumerable properties + * of this object, including properties from prototype objects. The + * array returned by this method contains the same values as would + * be enumerated by a for-in statement over this object. + */ + Local<Array> GetPropertyNames(); + + /** + * This function has the same functionality as GetPropertyNames but + * the returned array doesn't contain the names of properties from + * prototype objects. + */ + Local<Array> GetOwnPropertyNames(); + + /** + * Get the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + Local<Value> GetPrototype(); + + /** + * Set the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + bool SetPrototype(Handle<Value> prototype); + + /** Gets the value in an internal field. */ + Local<Value> GetInternalField(int index); + /** Sets the value in an internal field. */ + void SetInternalField(int index, Handle<Value> value); + + class V8EXPORT ExternalResource { // NOLINT + public: + ExternalResource() {} + virtual ~ExternalResource() {} + + virtual void Dispose() { delete this; } + + private: + // Disallow copying and assigning. + ExternalResource(const ExternalResource&); + void operator=(const ExternalResource&); + }; + + void SetExternalResource(ExternalResource *); + ExternalResource *GetExternalResource(); + + // Testers for local properties. + bool HasOwnProperty(Handle<String> key); + + /** + * Returns the identity hash for this object. The current implementation + * uses a hidden property on the object to store the identity hash. + * + * The return value will never be 0. Also, it is not guaranteed to be + * unique. + */ + int GetIdentityHash(); + + /** + * Access hidden properties on JavaScript objects. These properties are + * hidden from the executing JavaScript and only accessible through the V8 + * C++ API. Hidden properties introduced by V8 internally (for example the + * identity hash) are prefixed with "v8::". + */ + bool SetHiddenValue(Handle<String> key, Handle<Value> value); + Local<Value> GetHiddenValue(Handle<String> key); + + /** + * Clone this object with a fast but shallow copy. Values will point + * to the same values as the original object. + */ + Local<Object> Clone(); + + + /** + * Checks whether a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * When an Object is callable this method returns true. + */ + bool IsCallable(); + + /** + * Call an Object as a function if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + */ + Local<Value> CallAsFunction(Handle<Object> recv, + int argc, + Handle<Value> argv[]); + + /** + * Call an Object as a constructor if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * Note: This method behaves like the Function::NewInstance method. + */ + Local<Value> CallAsConstructor(int argc, + Handle<Value> argv[]); + + static Local<Object> New(); + static Object* Cast(Value* obj); +}; + + +/** + * An instance of the built-in array constructor (ECMA-262, 15.4.2). + */ +class V8EXPORT Array : public Object { + public: + uint32_t Length() const; + + /** + * Creates a JavaScript array with the given length. If the length + * is negative the returned array will have length 0. + */ + static Local<Array> New(int length = 0); + + static Array* Cast(Value* obj); +}; + + +/** + * A JavaScript function object (ECMA-262, 15.3). + */ +class V8EXPORT Function : public Object { + public: + Local<Object> NewInstance() const; + Local<Object> NewInstance(int argc, Handle<Value> argv[]) const; + Local<Value> Call(Handle<Object> recv, + int argc, + Handle<Value> argv[]); + Handle<Value> GetName() const; + + ScriptOrigin GetScriptOrigin() const; + static Function* Cast(Value* obj); +}; + + +/** + * An instance of the built-in Date constructor (ECMA-262, 15.9). + */ +class V8EXPORT Date : public Object { + public: + static Local<Value> New(double time); + + /** + * A specialization of Value::NumberValue that is more efficient + * because we know the structure of this object. + */ + double NumberValue() const; + + static Date* Cast(v8::Value* obj); + + /** + * Notification that the embedder has changed the time zone, + * daylight savings time, or other date / time configuration + * parameters. V8 keeps a cache of various values used for + * date / time computation. This notification will reset + * those cached values for the current context so that date / + * time configuration changes would be reflected in the Date + * object. + * + * This API should not be called more than needed as it will + * negatively impact the performance of date operations. + */ + static void DateTimeConfigurationChangeNotification(); + +}; + + +/** + * A Number object (ECMA-262, 4.3.21). + */ +class V8EXPORT NumberObject : public Object { + public: + static Local<Value> New(double value); + + /** + * Returns the Number held by the object. + */ + double NumberValue() const; + + static NumberObject* Cast(v8::Value* obj); + +}; + + +/** + * A Boolean object (ECMA-262, 4.3.15). + */ +class V8EXPORT BooleanObject : public Object { + public: + static Local<Value> New(bool value); + + /** + * Returns the Boolean held by the object. + */ + bool BooleanValue() const; + + static BooleanObject* Cast(v8::Value* obj); + +}; + + +/** + * A String object (ECMA-262, 4.3.18). + */ +class V8EXPORT StringObject : public Object { + public: + static Local<Value> New(Handle<String> value); + + /** + * Returns the String held by the object. + */ + Local<String> StringValue() const; + + static StringObject* Cast(v8::Value* obj); + +}; + + +/** + * An instance of the built-in RegExp constructor (ECMA-262, 15.10). + */ +class V8EXPORT RegExp : public Object { + public: + /** + * Regular expression flag bits. They can be or'ed to enable a set + * of flags. + */ + enum Flags { + kNone = 0, + kGlobal = 1, + kIgnoreCase = 2, + kMultiline = 4 + }; + + /** + * Creates a regular expression from the given pattern string and + * the flags bit field. May throw a JavaScript exception as + * described in ECMA-262, 15.10.4.1. + * + * For example, + * RegExp::New(v8::String::New("foo"), + * static_cast<RegExp::Flags>(kGlobal | kMultiline)) + * is equivalent to evaluating "/foo/gm". + */ + static Local<RegExp> New(Handle<String> pattern, + Flags flags); + + /** + * Returns the value of the source property: a string representing + * the regular expression. + */ + Local<String> GetSource() const; + + /** + * Returns the flags bit field. + */ + Flags GetFlags() const; + + static RegExp* Cast(v8::Value* obj); + +}; + + +/** + * A JavaScript value that wraps a C++ void*. This type of value is + * mainly used to associate C++ data structures with JavaScript + * objects. + * + * The Wrap function V8 will return the most optimal Value object wrapping the + * C++ void*. The type of the value is not guaranteed to be an External object + * and no assumptions about its type should be made. To access the wrapped + * value Unwrap should be used, all other operations on that object will lead + * to unpredictable results. + */ +class V8EXPORT External : public Value { + public: + static Local<Value> Wrap(void* data); + static void* Unwrap(Handle<Value> obj); + + static Local<External> New(void* value); + static External* Cast(Value* obj); + void* Value() const; +}; + + +// --- Templates --- + + +/** + * The superclass of object and function templates. + */ +class V8EXPORT Template : public Data { + public: + /** Adds a property to each instance created by this template.*/ + void Set(Handle<String> name, Handle<Value> value, + PropertyAttribute attributes = None); + void Set(const char* name, Handle<Value> value); + + struct Property { + Persistent<String> name; + Persistent<Value> value; + PropertyAttribute attributes; + }; + QVector<Property> m_properties; + }; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Template) + +/** + * The argument information given to function call callbacks. This + * class provides access to information about the context of the call, + * including the receiver, the number and values of arguments, and + * the holder of the function. + */ +class V8EXPORT Arguments { + public: + Arguments(const QQmlJS::VM::Value *args, int argc, const QQmlJS::VM::Value &thisObject, bool isConstructor, + const Persistent<Value> &data); + int Length() const; + Local<Value> operator[](int i) const; + Local<Object> This() const; + Local<Object> Holder() const; + bool IsConstructCall() const; + Local<Value> Data() const; + Isolate* GetIsolate() const; + +private: + QVector<Persistent<Value> > m_args; + Persistent<Object> m_thisObject; + bool m_isConstructor; + Persistent<Value> m_data; +}; + + +/** + * The information passed to an accessor callback about the context + * of the property access. + */ +class V8EXPORT AccessorInfo { + public: + AccessorInfo(const QQmlJS::VM::Value &thisObject, const Persistent<Value> &data); + Isolate* GetIsolate() const; + Local<Value> Data() const; + Local<Object> This() const; + Local<Object> Holder() const; +private: + Persistent<Value> m_this; + Persistent<Value> m_data; +}; + + +typedef Handle<Value> (*InvocationCallback)(const Arguments& args); + +/** + * NamedProperty[Getter|Setter] are used as interceptors on object. + * See ObjectTemplate::SetNamedPropertyHandler. + */ +typedef Handle<Value> (*NamedPropertyGetter)(Local<String> property, + const AccessorInfo& info); + + +/** + * Returns the value if the setter intercepts the request. + * Otherwise, returns an empty handle. + */ +typedef Handle<Value> (*NamedPropertySetter)(Local<String> property, + Local<Value> value, + const AccessorInfo& info); + +/** + * Returns a non-empty handle if the interceptor intercepts the request. + * The result is an integer encoding property attributes (like v8::None, + * v8::DontEnum, etc.) + */ +typedef Handle<Integer> (*NamedPropertyQuery)(Local<String> property, + const AccessorInfo& info); + + +/** + * Returns a non-empty handle if the deleter intercepts the request. + * The return value is true if the property could be deleted and false + * otherwise. + */ +typedef Handle<Boolean> (*NamedPropertyDeleter)(Local<String> property, + const AccessorInfo& info); + +/** + * Returns an array containing the names of the properties the named + * property getter intercepts. + */ +typedef Handle<Array> (*NamedPropertyEnumerator)(const AccessorInfo& info); + + +/** + * Returns the value of the property if the getter intercepts the + * request. Otherwise, returns an empty handle. + */ +typedef Handle<Value> (*IndexedPropertyGetter)(uint32_t index, + const AccessorInfo& info); + + +/** + * Returns the value if the setter intercepts the request. + * Otherwise, returns an empty handle. + */ +typedef Handle<Value> (*IndexedPropertySetter)(uint32_t index, + Local<Value> value, + const AccessorInfo& info); + + +/** + * Returns a non-empty handle if the interceptor intercepts the request. + * The result is an integer encoding property attributes. + */ +typedef Handle<Integer> (*IndexedPropertyQuery)(uint32_t index, + const AccessorInfo& info); + +/** + * Returns a non-empty handle if the deleter intercepts the request. + * The return value is true if the property could be deleted and false + * otherwise. + */ +typedef Handle<Boolean> (*IndexedPropertyDeleter)(uint32_t index, + const AccessorInfo& info); + +/** + * Returns an array containing the indices of the properties the + * indexed property getter intercepts. + */ +typedef Handle<Array> (*IndexedPropertyEnumerator)(const AccessorInfo& info); + + +/** + * A FunctionTemplate is used to create functions at runtime. There + * can only be one function created from a FunctionTemplate in a + * context. The lifetime of the created function is equal to the + * lifetime of the context. So in case the embedder needs to create + * temporary functions that can be collected using Scripts is + * preferred. + * + * A FunctionTemplate can have properties, these properties are added to the + * function object when it is created. + * + * A FunctionTemplate has a corresponding instance template which is + * used to create object instances when the function is used as a + * constructor. Properties added to the instance template are added to + * each object instance. + * + * A FunctionTemplate can have a prototype template. The prototype template + * is used to create the prototype object of the function. + * + * The following example shows how to use a FunctionTemplate: + * + * \code + * v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); + * t->Set("func_property", v8::Number::New(1)); + * + * v8::Local<v8::Template> proto_t = t->PrototypeTemplate(); + * proto_t->Set("proto_method", v8::FunctionTemplate::New(InvokeCallback)); + * proto_t->Set("proto_const", v8::Number::New(2)); + * + * v8::Local<v8::ObjectTemplate> instance_t = t->InstanceTemplate(); + * instance_t->SetAccessor("instance_accessor", InstanceAccessorCallback); + * instance_t->SetNamedPropertyHandler(PropertyHandlerCallback, ...); + * instance_t->Set("instance_property", Number::New(3)); + * + * v8::Local<v8::Function> function = t->GetFunction(); + * v8::Local<v8::Object> instance = function->NewInstance(); + * \endcode + * + * Let's use "function" as the JS variable name of the function object + * and "instance" for the instance object created above. The function + * and the instance will have the following properties: + * + * \code + * func_property in function == true; + * function.func_property == 1; + * + * function.prototype.proto_method() invokes 'InvokeCallback' + * function.prototype.proto_const == 2; + * + * instance instanceof function == true; + * instance.instance_accessor calls 'InstanceAccessorCallback' + * instance.instance_property == 3; + * \endcode + * + * A FunctionTemplate can inherit from another one by calling the + * FunctionTemplate::Inherit method. The following graph illustrates + * the semantics of inheritance: + * + * \code + * FunctionTemplate Parent -> Parent() . prototype -> { } + * ^ ^ + * | Inherit(Parent) | .__proto__ + * | | + * FunctionTemplate Child -> Child() . prototype -> { } + * \endcode + * + * A FunctionTemplate 'Child' inherits from 'Parent', the prototype + * object of the Child() function has __proto__ pointing to the + * Parent() function's prototype object. An instance of the Child + * function has all properties on Parent's instance templates. + * + * Let Parent be the FunctionTemplate initialized in the previous + * section and create a Child FunctionTemplate by: + * + * \code + * Local<FunctionTemplate> parent = t; + * Local<FunctionTemplate> child = FunctionTemplate::New(); + * child->Inherit(parent); + * + * Local<Function> child_function = child->GetFunction(); + * Local<Object> child_instance = child_function->NewInstance(); + * \endcode + * + * The Child function and Child instance will have the following + * properties: + * + * \code + * child_func.prototype.__proto__ == function.prototype; + * child_instance.instance_accessor calls 'InstanceAccessorCallback' + * child_instance.instance_property == 3; + * \endcode + */ +class V8EXPORT FunctionTemplate : public Template { + public: + /** Creates a function template.*/ + static Local<FunctionTemplate> New( + InvocationCallback callback = 0, + Handle<Value> data = Handle<Value>()); + /** Returns the unique function instance in the current execution context.*/ + Local<Function> GetFunction(); + + /** Get the InstanceTemplate. */ + Local<ObjectTemplate> InstanceTemplate(); + + /** + * A PrototypeTemplate is the template used to create the prototype object + * of the function created by this template. + */ + Local<ObjectTemplate> PrototypeTemplate(); + +private: + FunctionTemplate(InvocationCallback callback, Handle<Value> data); + friend class V4V8Function; + InvocationCallback m_callback; + Persistent<Value> m_data; + Local<ObjectTemplate> m_instanceTemplate; + Local<ObjectTemplate> m_prototypeTemplate; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(FunctionTemplate) + + +/** + * An ObjectTemplate is used to create objects at runtime. + * + * Properties added to an ObjectTemplate are added to each object + * created from the ObjectTemplate. + */ +class V8EXPORT ObjectTemplate : public Template { + public: + /** Creates an ObjectTemplate. */ + static Local<ObjectTemplate> New(); + + /** Creates a new instance of this template.*/ + Local<Object> NewInstance(); + + /** + * Sets an accessor on the object template. + * + * Whenever the property with the given name is accessed on objects + * created from this ObjectTemplate the getter and setter callbacks + * are called instead of getting and setting the property directly + * on the JavaScript object. + * + * \param name The name of the property for which an accessor is added. + * \param getter The callback to invoke when getting the property. + * \param setter The callback to invoke when setting the property. + * \param data A piece of data that will be passed to the getter and setter + * callbacks whenever they are invoked. + * \param settings Access control settings for the accessor. This is a bit + * field consisting of one of more of + * DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2. + * The default is to not allow cross-context access. + * ALL_CAN_READ means that all cross-context reads are allowed. + * ALL_CAN_WRITE means that all cross-context writes are allowed. + * The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all + * cross-context access. + * \param attribute The attributes of the property for which an accessor + * is added. + * \param signature The signature describes valid receivers for the accessor + * and is used to perform implicit instance checks against them. If the + * receiver is incompatible (i.e. is not an instance of the constructor as + * defined by FunctionTemplate::HasInstance()), an implicit TypeError is + * thrown and no callback is invoked. + */ + void SetAccessor(Handle<String> name, + AccessorGetter getter, + AccessorSetter setter = 0, + Handle<Value> data = Handle<Value>(), + AccessControl settings = DEFAULT, + PropertyAttribute attribute = None); + + /** + * Sets a named property handler on the object template. + * + * Whenever a named property is accessed on objects created from + * this object template, the provided callback is invoked instead of + * accessing the property directly on the JavaScript object. + * + * \param getter The callback to invoke when getting a property. + * \param setter The callback to invoke when setting a property. + * \param query The callback to invoke to check if a property is present, + * and if present, get its attributes. + * \param deleter The callback to invoke when deleting a property. + * \param enumerator The callback to invoke to enumerate all the named + * properties of an object. + * \param data A piece of data that will be passed to the callbacks + * whenever they are invoked. + */ + void SetNamedPropertyHandler(NamedPropertyGetter getter, + NamedPropertySetter setter = 0, + NamedPropertyQuery query = 0, + NamedPropertyDeleter deleter = 0, + NamedPropertyEnumerator enumerator = 0, + Handle<Value> data = Handle<Value>()); + void SetFallbackPropertyHandler(NamedPropertyGetter getter, + NamedPropertySetter setter = 0, + NamedPropertyQuery query = 0, + NamedPropertyDeleter deleter = 0, + NamedPropertyEnumerator enumerator = 0, + Handle<Value> data = Handle<Value>()); + + /** + * Sets an indexed property handler on the object template. + * + * Whenever an indexed property is accessed on objects created from + * this object template, the provided callback is invoked instead of + * accessing the property directly on the JavaScript object. + * + * \param getter The callback to invoke when getting a property. + * \param setter The callback to invoke when setting a property. + * \param query The callback to invoke to check if an object has a property. + * \param deleter The callback to invoke when deleting a property. + * \param enumerator The callback to invoke to enumerate all the indexed + * properties of an object. + * \param data A piece of data that will be passed to the callbacks + * whenever they are invoked. + */ + void SetIndexedPropertyHandler(IndexedPropertyGetter getter, + IndexedPropertySetter setter = 0, + IndexedPropertyQuery query = 0, + IndexedPropertyDeleter deleter = 0, + IndexedPropertyEnumerator enumerator = 0, + Handle<Value> data = Handle<Value>()); + + /** + * Gets the number of internal fields for objects generated from + * this template. + */ + int InternalFieldCount(); + + /** + * Sets the number of internal fields for objects generated from + * this template. + */ + void SetInternalFieldCount(int value); + + /** + * Sets whether the object can store an "external resource" object. + */ + bool HasExternalResource(); + void SetHasExternalResource(bool value); + + /** + * Mark object instances of the template as using the user object + * comparison callback. + */ + void MarkAsUseUserObjectComparison(); + + struct Accessor { + Persistent<Value> getter; + Persistent<Value> setter; + Persistent<String> name; + PropertyAttribute attribute; + }; + + QVector<Accessor> m_accessors; + + NamedPropertyGetter m_namedPropertyGetter; + NamedPropertySetter m_namedPropertySetter; + NamedPropertyQuery m_namedPropertyQuery; + NamedPropertyDeleter m_namedPropertyDeleter; + NamedPropertyEnumerator m_namedPropertyEnumerator; + Persistent<Value> m_namedPropertyData; + + NamedPropertyGetter m_fallbackPropertyGetter; + NamedPropertySetter m_fallbackPropertySetter; + NamedPropertyQuery m_fallbackPropertyQuery; + NamedPropertyDeleter m_fallbackPropertyDeleter; + NamedPropertyEnumerator m_fallbackPropertyEnumerator; + Persistent<Value> m_fallbackPropertyData; + + IndexedPropertyGetter m_indexedPropertyGetter; + IndexedPropertySetter m_indexedPropertySetter; + IndexedPropertyQuery m_indexedPropertyQuery; + IndexedPropertyDeleter m_indexedPropertyDeleter; + IndexedPropertyEnumerator m_indexedPropertyEnumerator; + Persistent<Value> m_indexedPropertyData; + + bool m_useUserComparison; + private: + ObjectTemplate(); + }; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(ObjectTemplate) + +// --- Statics --- + + +Handle<Primitive> V8EXPORT Undefined(); +Handle<Primitive> V8EXPORT Null(); +Handle<Boolean> V8EXPORT True(); +Handle<Boolean> V8EXPORT False(); + +inline Handle<Primitive> Undefined(Isolate*) { return Undefined(); } +inline Handle<Primitive> Null(Isolate*) { return Null(); } +inline Handle<Boolean> True(Isolate*) { return True(); } +inline Handle<Boolean> False(Isolate*) { return False(); } + + + +// --- Exceptions --- + + +/** + * Schedules an exception to be thrown when returning to JavaScript. When an + * exception has been scheduled it is illegal to invoke any JavaScript + * operation; the caller must return immediately and only after the exception + * has been handled does it become legal to invoke JavaScript operations. + */ +Handle<Value> V8EXPORT ThrowException(Handle<Value> exception); + +/** + * Create new error objects by calling the corresponding error object + * constructor with the message. + */ +class V8EXPORT Exception { + public: + static Local<Value> ReferenceError(Handle<String> message); + static Local<Value> SyntaxError(Handle<String> message); + static Local<Value> TypeError(Handle<String> message); + static Local<Value> Error(Handle<String> message); +}; + + +// --- User Object Comparison Callback --- +typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs, + Local<Object> rhs); + +// --- Garbage Collection Callbacks --- + +/** + * Applications can register callback functions which will be called + * before and after a garbage collection. Allocations are not + * allowed in the callback functions, you therefore cannot manipulate + * objects (set or delete properties for example) since it is possible + * such operations will result in the allocation of objects. + */ +enum GCType { + kGCTypeScavenge = 1 << 0, + kGCTypeMarkSweepCompact = 1 << 1, + kGCTypeAll = kGCTypeScavenge | kGCTypeMarkSweepCompact +}; + +enum GCCallbackFlags { + kNoGCCallbackFlags = 0, + kGCCallbackFlagCompacted = 1 << 0 +}; + +typedef void (*GCPrologueCallback)(GCType type, GCCallbackFlags flags); +typedef void (*GCCallback)(); + + + +/** + * Isolate represents an isolated instance of the V8 engine. V8 + * isolates have completely separate states. Objects from one isolate + * must not be used in other isolates. When V8 is initialized a + * default isolate is implicitly created and entered. The embedder + * can create additional isolates and use them in parallel in multiple + * threads. An isolate can be entered by at most one thread at any + * given time. The Locker/Unlocker API must be used to synchronize. + */ +class V8EXPORT Isolate { + public: + Isolate(); + ~Isolate(); + /** + * Stack-allocated class which sets the isolate for all operations + * executed within a local scope. + */ + class V8EXPORT Scope { + public: + explicit Scope(Isolate* isolate) : isolate_(isolate) { + isolate->Enter(); + } + + ~Scope() { isolate_->Exit(); } + + private: + Isolate* const isolate_; + + // Prevent copying of Scope objects. + Scope(const Scope&); + Scope& operator=(const Scope&); + }; + + /** + * Creates a new isolate. Does not change the currently entered + * isolate. + * + * When an isolate is no longer used its resources should be freed + * by calling Dispose(). Using the delete operator is not allowed. + */ + static Isolate* New(); + + /** + * Returns the entered isolate for the current thread or NULL in + * case there is no current isolate. + */ + static Isolate* GetCurrent(); + + /** + * Methods below this point require holding a lock (using Locker) in + * a multi-threaded environment. + */ + + /** + * Sets this isolate as the entered one for the current thread. + * Saves the previously entered one (if any), so that it can be + * restored when exiting. Re-entering an isolate is allowed. + */ + void Enter(); + + /** + * Exits this isolate by restoring the previously entered one in the + * current thread. The isolate may still stay the same, if it was + * entered more than once. + * + * Requires: this == Isolate::GetCurrent(). + */ + void Exit(); + + /** + * Disposes the isolate. The isolate must not be entered by any + * thread to be disposable. + */ + void Dispose(); + + /** + * Associate embedder-specific data with the isolate + */ + void SetData(void* data); + + /** + * Retrieve embedder-specific data from the isolate. + * Returns NULL if SetData has never been called. + */ + void* GetData(); + + Context *GetCurrentContext() { return m_contextStack.top(); } + void setException(const QQmlJS::VM::Value &ex); + + private: + friend class Context; + friend class TryCatch; + Isolate* m_lastIsolate; + QStack<Context*> m_contextStack; + TryCatch *tryCatch; +}; + + +/** + * Container class for static utility functions. + */ +class V8EXPORT V8 { + public: + + /** + * Sets V8 flags from a string. + */ + static void SetFlagsFromString(const char* str, int length); + + /** Callback for user object comparisons */ + static void SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback); + + /** + * Enables the host application to receive a notification before a + * garbage collection. Allocations are not allowed in the + * callback function, you therefore cannot manipulate objects (set + * or delete properties for example) since it is possible such + * operations will result in the allocation of objects. It is possible + * to specify the GCType filter for your callback. But it is not possible to + * register the same callback function two times with different + * GCType filters. + */ + static void AddGCPrologueCallback( + GCPrologueCallback callback, GCType gc_type_filter = kGCTypeAll); + + /** + * This function removes callback which was installed by + * AddGCPrologueCallback function. + */ + static void RemoveGCPrologueCallback(GCPrologueCallback callback); + + /** + * Allows the host application to declare implicit references between + * the objects: if |parent| is alive, all |children| are alive too. + * After each garbage collection, all implicit references + * are removed. It is intended to be used in the before-garbage-collection + * callback function. + */ + static void AddImplicitReferences(Persistent<Object> parent, + Persistent<Value>* children, + size_t length); + + /** + * Initializes from snapshot if possible. Otherwise, attempts to + * initialize from scratch. This function is called implicitly if + * you use the API without calling it first. + */ + static bool Initialize(); + + /** + * Releases any resources used by v8 and stops any utility threads + * that may be running. Note that disposing v8 is permanent, it + * cannot be reinitialized. + * + * It should generally not be necessary to dispose v8 before exiting + * a process, this should happen automatically. It is only necessary + * to use if the process needs the resources taken up by v8. + */ + static bool Dispose(); + + /** + * Optional notification that the embedder is idle. + * V8 uses the notification to reduce memory footprint. + * This call can be used repeatedly if the embedder remains idle. + * Returns true if the embedder should stop calling IdleNotification + * until real work has been done. This indicates that V8 has done + * as much cleanup as it will be able to do. + * + * The hint argument specifies the amount of work to be done in the function + * on scale from 1 to 1000. There is no guarantee that the actual work will + * match the hint. + */ + static bool IdleNotification(int hint = 1000); + + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to attempt to free memory. + */ + static void LowMemoryNotification(); +}; + +/** + * An external exception handler. + */ +class V8EXPORT TryCatch { + public: + /** + * Creates a new try/catch block and registers it with v8. + */ + TryCatch(); + + /** + * Unregisters and deletes this try/catch block. + */ + ~TryCatch(); + + /** + * Returns true if an exception has been caught by this try/catch block. + */ + bool HasCaught() const; + + /** + * Throws the exception caught by this TryCatch in a way that avoids + * it being caught again by this same TryCatch. As with ThrowException + * it is illegal to execute any JavaScript operations after calling + * ReThrow; the caller must return immediately to where the exception + * is caught. + */ + Handle<Value> ReThrow(); + + /** + * Returns the exception caught by this try/catch block. If no exception has + * been caught an empty handle is returned. + * + * The returned handle is valid until this TryCatch block has been destroyed. + */ + Local<Value> Exception() const; + + /** + * Returns the message associated with this exception. If there is + * no message associated an empty handle is returned. + * + * The returned handle is valid until this TryCatch block has been + * destroyed. + */ + Local<v8::Message> Message() const; + + /** + * Clears any exceptions that may have been caught by this try/catch block. + * After this method has been called, HasCaught() will return false. + * + * It is not necessary to clear a try/catch block before using it again; if + * another exception is thrown the previously caught exception will just be + * overwritten. However, it is often a good idea since it makes it easier + * to determine which operation threw a given exception. + */ + void Reset(); + +private: + friend class Isolate; + TryCatch *parent; + bool hasCaughtException; + Local<Value> exception; +}; + + +// --- Context --- +class V8EXPORT ExtensionConfiguration; + +/** + * A sandboxed execution context with its own set of built-in objects + * and functions. + */ +class V8EXPORT Context : public QSharedData { + public: + Context(); + ~Context(); + + static Local<Context> Adopt(Context *p) + { + Local<Context> l; + l.object = p; + l.object->ref.ref(); + return l; + } + /** + * Returns the global proxy object or global object itself for + * detached contexts. + * + * Global proxy object is a thin wrapper whose prototype points to + * actual context's global object with the properties like Object, etc. + * This is done that way for security reasons (for more details see + * https://wiki.mozilla.org/Gecko:SplitWindow). + * + * Please note that changes to global proxy object prototype most probably + * would break VM---v8 expects only global object as a prototype of + * global proxy object. + * + * If DetachGlobal() has been invoked, Global() would return actual global + * object until global is reattached with ReattachGlobal(). + */ + Local<Object> Global(); + + /** Creates a new context. + * + * Returns a persistent handle to the newly allocated context. This + * persistent handle has to be disposed when the context is no + * longer used so the context can be garbage collected. + * + * \param extensions An optional extension configuration containing + * the extensions to be installed in the newly created context. + * + * \param global_template An optional object template from which the + * global object for the newly created context will be created. + * + * \param global_object An optional global object to be reused for + * the newly created context. This global object must have been + * created by a previous call to Context::New with the same global + * template. The state of the global object will be completely reset + * and only object identify will remain. + */ + static Persistent<Context> New( + ExtensionConfiguration* extensions = NULL, + Handle<ObjectTemplate> global_template = Handle<ObjectTemplate>(), + Handle<Value> global_object = Handle<Value>()); + + /** Returns the context that is on the top of the stack. */ + static Local<Context> GetCurrent(); + + /** + * Returns the context of the calling JavaScript code. That is the + * context of the top-most JavaScript frame. If there are no + * JavaScript frames an empty handle is returned. + */ + static Local<Context> GetCalling(); + static Local<Object> GetCallingQmlGlobal(); + static Local<Value> GetCallingScriptData(); + + /** + * Enter this context. After entering a context, all code compiled + * and run is compiled and run in this context. If another context + * is already entered, this old context is saved so it can be + * restored when the new context is exited. + */ + void Enter(); + + /** + * Exit this context. Exiting the current context restores the + * context that was in place when entering the current context. + */ + void Exit(); + + /** + * Associate an additional data object with the context. This is mainly used + * with the debugger to provide additional information on the context through + * the debugger API. + */ + void SetData(Handle<Value> data); + Local<Value> GetData(); + + /** + * Stack-allocated class which sets the execution context for all + * operations executed within a local scope. + */ + class Scope { + public: + explicit Scope(Handle<Context> context) : context_(context) { + context_->Enter(); + } + ~Scope() { context_->Exit(); } + private: + Handle<Context> context_; + }; + + QQmlJS::VM::ExecutionEngine *GetEngine(); + +private: + Context* m_lastContext; + struct Private; + Private *d; + friend class Value; + friend class Script; + friend class Object; + friend class Function; +}; + +DEFINE_REFCOUNTED_HANDLE_OPERATIONS(Context) + +template<typename T> +void Persistent<T>::MakeWeak(void* parameters, WeakReferenceCallback callback) +{ + Q_UNUSED(parameters); + Q_UNUSED(callback); + + Q_UNIMPLEMENTED(); +} + + + +} // namespace v8 + + +#undef V8EXPORT +#undef TYPE_CHECK + + +#endif // V8_H_ diff --git a/src/qml/qml/v4vm/qv4value.cpp b/src/qml/qml/v4vm/qv4value.cpp new file mode 100644 index 0000000000..73efe7d22d --- /dev/null +++ b/src/qml/qml/v4vm/qv4value.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4engine.h> +#include <qv4object.h> +#include <qv4objectproto.h> +#include "qv4mm.h" + +#include <wtf/MathExtras.h> + +namespace QQmlJS { +namespace VM { + + +int Value::toUInt16() const +{ + if (isConvertibleToInt()) + return (ushort)(uint)integerValue(); + + double number = __qmljs_to_number(*this); + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast<ushort>(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; +} + +double Value::toInteger() const +{ + if (isConvertibleToInt()) + return int_32; + + return Value::toInteger(__qmljs_to_number(*this)); +} + +double Value::toNumber() const +{ + return __qmljs_to_number(*this); +} + +bool Value::sameValue(Value other) const { + if (val == other.val) + return true; + if (isString() && other.isString()) + return stringValue()->isEqualTo(other.stringValue()); + if (isInteger()) + return int_32 ? (double(int_32) == other.dbl) : (other.val == 0); + if (other.isInteger()) + return other.int_32 ? (dbl == double(other.int_32)) : (val == 0); + return false; +} + +Value Value::fromString(ExecutionContext *ctx, const QString &s) +{ + return fromString(ctx->engine->newString(s)); +} + +int Value::toInt32(double number) +{ + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast<int>(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); +} + +unsigned int Value::toUInt32(double number) +{ + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast<uint>(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); +} + +double Value::toInteger(double number) +{ + if (isnan(number)) + return +0; + else if (! number || isinf(number)) + return number; + const double v = floor(fabs(number)); + return std::signbit(number) ? -v : v; +} + +Value Value::property(ExecutionContext *ctx, String *name) const +{ + return isObject() ? objectValue()->get(ctx, name) : undefinedValue(); +} + +PersistentValue::PersistentValue() + : m_memoryManager(0) + , m_value(Value::undefinedValue()) +{ +} + +PersistentValue::PersistentValue(MemoryManager *mm, const Value &val) + : m_memoryManager(mm) + , m_value(val) +{ + assert(mm); + if (Managed *m = asManaged()) + m_memoryManager->protect(m); +} + +PersistentValue::PersistentValue(const PersistentValue &other) + : m_memoryManager(other.m_memoryManager) + , m_value(other.m_value) +{ + if (Managed *m = asManaged()) + m_memoryManager->protect(m); +} + +PersistentValue &PersistentValue::operator=(const PersistentValue &other) +{ + if (this == &other) + return *this; + if (Managed *m = asManaged()) + m_memoryManager->unprotect(m); + m_memoryManager = other.m_memoryManager; + m_value = other.m_value; + if (Managed *m = asManaged()) + m_memoryManager->protect(m); +} + +PersistentValue::~PersistentValue() +{ + if (Managed *m = asManaged()) + m_memoryManager->unprotect(m); +} + + + +} // namespace VM +} // namespace QQmlJS diff --git a/src/qml/qml/v4vm/qv4value.h b/src/qml/qml/v4vm/qv4value.h new file mode 100644 index 0000000000..bbfba842b5 --- /dev/null +++ b/src/qml/qml/v4vm/qv4value.h @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_VALUE_H +#define QMLJS_VALUE_H + +#include <QtCore/QString> +#include <QtCore/qnumeric.h> +#include "qv4global.h" +#include "qv4string.h" +#include <QtCore/QDebug> +#include "qv4managed.h" + +#include <wtf/MathExtras.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace VM { + +struct String; +struct ExecutionContext; +struct ExecutionEngine; +struct Value; + +extern "C" { +double __qmljs_to_number(const Value &value); +Q_V4_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); +} + +typedef uint Bool; + + +struct Q_V4_EXPORT Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if QT_POINTER_SIZE == 4 + Managed *m; + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NotDouble_Mask = 0xfffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + Special_Mask = Immediate_Mask | 0x20000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000, + Deleted_Type = NotDouble_Mask | 0x30000, + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Deleted_Type = Deleted_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline unsigned type() const { + return tag & Type_Mask; + } + + // used internally in property + inline bool isDeleted() const { return tag == _Deleted_Type; } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if QT_POINTER_SIZE == 8 + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + + Bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if QT_POINTER_SIZE == 8 + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Managed *managed() const { + return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } + Managed *managed() const { + return m; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value deletedValue(); + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromUInt32(uint i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(ExecutionContext *ctx, const QString &fromString); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + int toUInt16() const; + int toInt32() const; + unsigned int toUInt32() const; + + Bool toBoolean() const; + double toInteger() const; + double toNumber() const; + String *toString(ExecutionContext *ctx) const; + Object *toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } +#if QT_POINTER_SIZE == 8 + inline bool integerCompatible() const { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return (val & mask) == mask; + } + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else + inline bool integerCompatible() const { + return (tag & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } +#endif + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + String *asString() const; + Managed *asManaged() const; + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + RegExpObject *asRegExpObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + uint asArrayIndex() const; + uint asArrayLength(bool *ok) const; + + Value property(ExecutionContext *ctx, String *name) const; + + // Section 9.12 + bool sameValue(Value other) const; + + void mark() const { + Managed *m = asManaged(); + if (m) + m->mark(); + } +}; + +inline Value Value::undefinedValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::nullValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline VM::Value Value::deletedValue() +{ + VM::Value v; + v.tag = VM::Value::_Deleted_Type; + v.uint_32 = 0; + return v; +} + + +inline Value Value::fromBoolean(Bool b) +{ + Value v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Value Value::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value Value::fromInt32(int i) +{ + Value v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Value Value::fromUInt32(uint i) +{ + Value v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.dbl = i; + } + return v; +} + +inline Value Value::fromString(String *s) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)s; + v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif + return v; +} + +inline Value Value::fromObject(Object *o) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)o; + v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif + return v; +} + +inline Bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32; + case Value::String_Type: + return stringValue()->toQString().length() > 0; + case Value::Object_Type: + return true; + default: // double + if (! doubleValue() || isnan(doubleValue())) + return false; + return true; + } +} + +inline String *Value::toString(ExecutionContext *ctx) const +{ + if (isString()) + return stringValue(); + return __qmljs_convert_to_string(ctx, *this); +} + +inline Object *Value::toObject(ExecutionContext *ctx) const +{ + if (isObject()) + return objectValue(); + return __qmljs_convert_to_object(ctx, *this); +} + +inline int Value::toInt32() const +{ + if (isConvertibleToInt()) + return int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast<int>(d); + + return Value::toInt32(__qmljs_to_number(*this)); +} + +inline unsigned int Value::toUInt32() const +{ + if (isConvertibleToInt()) + return (unsigned) int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + if (dbl >= 0 && dbl < D32) + return static_cast<uint>(dbl); + return toUInt32(d); +} + +inline uint Value::asArrayIndex() const +{ + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; + uint idx = (uint)dbl; + if (idx != dbl) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isConvertibleToInt() && int_32 >= 0) + return (uint)int_32; + if (isDouble()) { + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} + +inline String *Value::asString() const +{ + if (isString()) + return stringValue(); + return 0; +} + +inline Managed *Value::asManaged() const +{ + if (isObject() || isString()) + return managed(); + return 0; +} + +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? managed()->asBooleanObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline RegExpObject *Value::asRegExpObject() const +{ + return isObject() ? managed()->asRegExpObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + +// ### +inline Value Managed::construct(ExecutionContext *context, Value *args, int argc) { + return vtbl->construct(this, context, args, argc); +} +inline Value Managed::call(ExecutionContext *context, const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, context, thisObject, args, argc); +} + +class PersistentValue +{ +public: + PersistentValue(); + PersistentValue(MemoryManager *mm, const Value &val); + PersistentValue(const PersistentValue &other); + PersistentValue &operator=(const PersistentValue &other); + ~PersistentValue(); + + Value *operator->() { return &m_value; } + Value *operator*() { return &m_value; } + + operator Value() const { return m_value; } + +private: + Managed *asManaged() { return m_memoryManager ? m_value.asManaged() : 0; } + MemoryManager *m_memoryManager; + Value m_value; +}; + +} // namespace VM +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4vm/v4.pri b/src/qml/qml/v4vm/v4.pri new file mode 100644 index 0000000000..3b9711bdb6 --- /dev/null +++ b/src/qml/qml/v4vm/v4.pri @@ -0,0 +1,8 @@ +include(llvm_installation.pri) +include(../3rdparty/masm/masm-defs.pri) + +CONFIG += exceptions + +!llvm: DEFINES += QMLJS_NO_LLVM + +INCLUDEPATH += $$PWD diff --git a/src/qml/qml/v4vm/v4.pro b/src/qml/qml/v4vm/v4.pro new file mode 100644 index 0000000000..325a1013b1 --- /dev/null +++ b/src/qml/qml/v4vm/v4.pro @@ -0,0 +1,177 @@ +TARGET = QtV4 +QT_PRIVATE = core-private qmldevtools-private +QT = core + +CONFIG += internal_module + +include(v4.pri) + +load(qt_build_config) +load(qt_module) + +CONFIG += warn_off + +#win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 #TODO ??! + +!macx-clang*:!win*:LIBS += -rdynamic + +SOURCES += \ + qv4codegen.cpp \ + qv4jsir.cpp \ + qv4engine.cpp \ + qv4context.cpp \ + qv4runtime.cpp \ + qv4value.cpp \ + qv4syntaxchecker.cpp \ + qv4isel_masm.cpp \ + llvm_runtime.cpp \ + qv4isel_p.cpp \ + debugging.cpp \ + qv4lookup.cpp \ + qv4mm.cpp \ + qv4managed.cpp \ + qv4internalclass.cpp \ + qv4sparsearray.cpp \ + qv4arrayobject.cpp \ + qv4argumentsobject.cpp \ + qv4booleanobject.cpp \ + qv4dateobject.cpp \ + qv4errorobject.cpp \ + qv4functionobject.cpp \ + qv4globalobject.cpp \ + qv4jsonobject.cpp \ + qv4mathobject.cpp \ + qv4numberobject.cpp \ + qv4object.cpp \ + qv4objectproto.cpp \ + qv4regexpobject.cpp \ + qv4stringobject.cpp \ + qv4string.cpp \ + qv4objectiterator.cpp \ + qv4regexp.cpp \ + qv4unwindhelper.cpp \ + qv4v8.cpp \ + qv4executableallocator.cpp + +HEADERS += \ + qv4global.h \ + qv4codegen_p.h \ + qv4jsir_p.h \ + qv4engine.h \ + qv4context.h \ + qv4runtime.h \ + qv4math.h \ + qv4value.h \ + qv4syntaxchecker_p.h \ + qv4isel_masm_p.h \ + qv4isel_p.h \ + qv4isel_util_p.h \ + debugging.h \ + qv4lookup.h \ + qv4identifier.h \ + qv4mm.h \ + qv4managed.h \ + qv4internalclass.h \ + qv4sparsearray.h \ + qv4arrayobject.h \ + qv4argumentsobject.h \ + qv4booleanobject.h \ + qv4dateobject.h \ + qv4errorobject.h \ + qv4functionobject.h \ + qv4globalobject.h \ + qv4jsonobject.h \ + qv4mathobject.h \ + qv4numberobject.h \ + qv4object.h \ + qv4objectproto.h \ + qv4regexpobject.h \ + qv4stringobject.h \ + qv4string.h \ + qv4property.h \ + qv4objectiterator.h \ + qv4regexp.h \ + qv4unwindhelper.h \ + qv4unwindhelper_p-dw2.h \ + qv4unwindhelper_p-arm.h \ + qv4v8.h \ + qcalculatehash_p.h \ + qv4util.h \ + qv4executableallocator.h + +llvm-libs { + +SOURCES += \ + qv4isel_llvm.cpp + +HEADERS += \ + qv4isel_llvm_p.h \ + qv4_llvm_p.h + +LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc +DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\"" +DEFINES += QMLJS_WITH_LLVM + +INCLUDEPATH += \ + $$system($$LLVM_CONFIG --includedir) + +QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden +QMAKE_CXXFLAGS -= -pedantic +QMAKE_CXXFLAGS -= -Wcovered-switch-default + +LIBS += \ + $$system($$LLVM_CONFIG --ldflags) \ + $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native) + +QMAKE_EXTRA_TARGETS += gen_llvm_runtime + +GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) +GEN_LLVM_RUNTIME_FLAGS -= -pedantic + +gen_llvm_runtime.target = llvm_runtime +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I$$PWD -I$$PWD/../3rdparty/masm $$join(QT.core.includes, " -I", "-I") $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC +} + +# Use SSE2 floating point math on 32 bit instead of the default +# 387 to make test results pass on 32 and on 64 bit builds. +linux-g++*:isEqual(QT_ARCH,i386) { + QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse + QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse +} + +TESTSCRIPT=$$PWD/../../tests/test262.py +V4CMD = v4 + +checktarget.target = check +checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations +checktarget.depends = all +QMAKE_EXTRA_TARGETS += checktarget + +checkmothtarget.target = check-interpreter +checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations +checkmothtarget.depends = all +QMAKE_EXTRA_TARGETS += checkmothtarget + +linux*|mac { + LIBS += -ldl +} + +debug-with-libunwind { + UW_INC=$$(LIBUNWIND_INCLUDES) + isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES") + INCLUDEPATH += $$UW_INC + UW_LIBS=$$(LIBUNWIND_LIBS) + isEmpty(UW_LIBS): error("Please set LIBUNWIND_LIBS") + LIBS += -L$$UW_LIBS + equals(QT_ARCH, arm): LIBS += -lunwind-arm + LIBS += -lunwind-dwarf-common -lunwind-dwarf-local -lunwind-elf32 -lunwind + DEFINES += WTF_USE_LIBUNWIND_DEBUG=1 +} + +valgrind { + DEFINES += V4_USE_VALGRIND +} + +include(moth/moth.pri) +include(../3rdparty/masm/masm.pri) +include(../3rdparty/double-conversion/double-conversion.pri) |